Python 的魔法方法(也称为特殊方法)允许我们自定义对象的行为。这些方法通常以双下划线包围,如
__init__
和
__str__
。了解和使用这些魔法方法可以使我们的自定义类在 Python 中表现得像内置类型一样。以下是一些常用的魔法方法及其用途:
1. 对象创建与初始化
2. 属性访问与管理
3. 字符串表示
4. 数值运算
-
__add__, __sub__, __mul__, __truediv__, __floordiv__: 分别处理加法、减法、乘法、真除法和整数除法(地板除法)。
-
__radd__, __rsub__, __rmul__, __rtruediv__, __rfloordiv__: 反射版本,处理操作数顺序颠倒的情况。
-
__iadd__, __isub__, __imul__, __itruediv__, __ifloordiv__: 处理复合赋值运算符(如 +=、-= 等)。
5. 位运算
6. 矩阵运算
7. 类与继承
通过理解和使用这些魔法方法,我们可以灵活地控制自定义对象的行为,使它们能够适应各种编程需求。接下来我们详细示例一下。
一:数值运算符
默认情况下,自定义对象不支持 +、-、* 等数值运算符。可以通过定义某些魔法方法来实现这一功能:
-
__add__ 定义了对象与值相加时的行为 (object + value)
-
__sub__ 定义了对象与值相减时的行为 (object - value)
-
__mul__ 定义了对象与值相乘时的行为 (object * value)
-
__truediv__ 定义了对象与值做真除法时的行为 (object / value)
-
__floordiv__ 定义了对象与值做整数除法时(地板除法)的行为 (object // value)
class Dog:
def __add__(self,x):
return f'+ {x} called'
def __sub__(self,x):
return f'- {x} called'
def __mul__(self,x):
return f'* {x} called'
def __truediv__(self,x):
return f'/ {x} called'
def __floordiv__(self,x):
return f'// {x} called'
dog = Dog()
print(dog + 2)
print(dog - 2)
print(dog * 2)
print(dog /2)
print(dog //2)
比如一个实际的例子:在处理文件路径的 Python 库 pathlib 中,我们可以使用
/
与 Path 对象一起将路径拼接在一起,尽管
/
通常是一个真除法运算符。
import pathlib
p=pathlib.Path('/a/b')
print(p)
print(p/ 'c')
print(p / 'apple')
二:反射数值运算符
对于大多数数值运算符,都有一个对应的反射版本。
-
__add__ 处理 object + value
-
__radd__(反射的 __add__)处理 value + object
-
__sub__ 处理 object - value
-
__rsub__(反射的 __sub__)处理 value - object
class Dog:
def __add__(self,x):
return f'self + {x}'
def __radd__(self,x):
return f'{x} + self'
def __sub__(self,x):
return f'self - {x}'
def __rsub__(self,x):
return f'{x} - self'
dog = Dog()
print( dog + 1 )
print( 1 + dog )
print( dog - 1 )
print( 1 - dog )
三:复合赋值运算符
上面的示例是处理
object + 5
和
5 + object
,那么
object += 5
又该如何处理呢?
对于复合赋值运算符,我们也有相应的魔法方法。
-
__iadd__ 处理 object += value
-
__isub__ 处理 object -= value
-
__imul__ 处理 object *= value
-
__itruediv__ 处理 object /= value
-
__ifloordiv__ 处理 object //= value
class Dog:
def __iadd__(self,x):
return f'self += {x}'
def __isub__(self,x):
return f'self -= {x}'
dog = Dog()
dog += 1
print(dog)
print(type(dog))
dog = Dog()
dog -= 1
print(dog)
print(type(dog))
四:__init__ VS __new__
__init__
和
__new__
都是用于初始化类对象的重要魔法方法,但它们并不相同。
下面是一个同时重写
__new__
和
__init__
的简单示例:
class Dog:
def __new__(cls,*args,**kwargs):
print(f'running __new__ {args} {kwargs}')
return super().__new__(cls)
def __init__(self,name,age):
print('running __init__')
self.name = name
self.age = age
dog = Dog('rocky',3)
__new__
和
__init__
的一些显著区别
-
__new__ 在 __init__ 之前运行。
-
__new__ 返回实例,而 __init__ 必须返回 None。
-
__new__ 创建并返回实例,而 __init__ 负责为新创建的实例分配属性(如 name、age 等)。
-
__new__ 是一个类方法,而 __init__ 是一个实例方法。
什么时候使用每个方法:
五:__getattr__ VS __getattribute__ VS __delattr__
这两个方法都与通过
object.attribute
访问对象的属性有关,但它们之间有一个细微的区别。
class Dog:
def __init__(self,name,age):
self.name = name
self.age = age
def __getattr__(self,name):
return f'{name} not found!'
dog = Dog('rocky',3)
print(dog.name)
print(dog.age)
print(dog.gender)
print(dog.breed)