什么是魔术方法?

在Python中,所有以双下划线__包起来的方法,统称为Magic Method(魔术方法),它是一种的特殊方法,普通方法需要调用,而魔术方法不需要调用就可以自动执行。或许在之前,你不知道这个概念,但我保证,只要你用过Python,那你一定在不经意间用过魔术方法。例如:对于两个字符串,你可能会用“+”号把他们连起来,得到一个合起来之后的字符串:

1
2
3
4
5
6
7
a = 'Hello, '
b = 'World!'
c = a + b
print(c)

>>>
Hello, World!

但是,按理说,“+”只能对于数使用,字符串之间是不能相加的,那为什么上面那段程序还能够运行呢?这就是魔术方法的功劳了。事实上,因为str类实现了__add__魔术方法,进而实现了运算符重载,所以我们可以通过“+”来实现字符串的拼接。

魔术方法在类或对象的某些事件触发后会自动执行,让类具有神奇的“魔力”。如果希望根据自己的程序定制自己特殊功能的类,那么就需要对这些方法进行重写。例如,如果我们想要实现一个自己的字符串类,在两个长度相等的全是字母的字符串相加时,返回一个结果字符串,它的每个字符是两个字符串相应位置字母表排序更大的那个字母,那么我们就可以重写__add__魔术方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Mystr(str):
def __init__(self, s):
self.s = s

def __add__(self, other):
res = []
if len(self.s) != len(other):
raise ValueError
else:
for i in range(len(self.s)):
if self.s[i] > other[i]:
res.append(self.s[i])
else:
res.append(other[i])
# 此处可简化为
# res = [self.s[i] if self.s[i] > other[i]
# else other[i] for i in range(len(self.s))]
return ''.join(res)

a = Mystr('jasndkcaww')
b = Mystr('lopergvmei')
print(a + b)

>>>
losnrkvmww

常用的魔术方法

1. 初始化方法____init____

1
2
3
4
触发机制:实例化对象之后立即触发
参数:至少有一个self,接收当前对象,其他参数根据需要进行定义
返回值:无
作用:初始化对象的成员

2. 获取对象长度__len__

1
2
3
4
5
触发时机:使用len(对象) 的时候触发
参数:一个参数self
返回值:必须是一个整型
作用:可以设置为检测对象成员个数,但是也可以进行其他任意操作
注意:返回值必须必须是整数,否则语法报错,另外该要求是格式要求。

3. 构造对象的字符串表示__str__与__repr__

1
2
3
4
5
触发时机:__str__:使用print(对象)或者str(对象),__repr__:repr(对象)的时候触发
参数:一个self接收对象
返回值:必须是字符串类型
作用:print(对象时)进行操作,得到字符串,通常用于快捷操作
注意:__repr__更严谨,是主要给机器用的格式,而__str__主要是给用户看的格式

未完待续……

魔术方法大全

方法名 参数列表 调用时机 返回值类型 备注
__repr__ self 当调用repr()函数时 str 返回值应为重建该对象的Python表达式(如果可能),或包含其他信息的字符串<...>
__str__ self 当调用str()函数时 str 调用print()等其他函数时会隐式调用__str__(),当不存在__str__()时会自动调用__repr__()
__format__ self, format_spec 当调用format()函数时 str object.__format__(x, '')等价于str(x)
__bytes__ self 当调用bytes()函数时 bytes 返回值是对象的字节表示
__bool__ self 调用bool()函数时 bool __bool__函数没有定义时,会隐式 调用__len__,如果__len__也没有 定义,则返回True
__complex__ self 调用commplex()函数时 complex
__int__ self 调用int()函数时 int
__float__ self 调用float()函数时 float
__hash__ self 调用hash() 函数时 hash()函数将__hash__()的返回值 截断到Py_ssize_t的范围内,64位系 统下Py_ssize_t为8字节;set等类 型的操作也会调用对象的__hash__函 数
__index__ self 调用operator.index()或Python需要整数值时 int __int____float____complex__没有实现时,这些函数隐式地调用__index__
__len__ self 调用len()函数时 int 返回值必须为非负整数,在CPython中返回值不能超过sys.maxsize中定义的值,否则len()等函数会抛出OverflowError等异常
__getitemm__ self, key 使用[]运算符时(右值) key可以为合法的数组下标,也可以是反向的下标,也可以是序列的切片,该函数应在key类型不对应时抛出TypeErrorkey超出范围时抛出IndexErrorKeyError(对于映射类型)
__setitem__ self, key, value 使用[]运算符时(左值) None key参数的取值及异常处理与__getitem__函数相同
__delitem__ self, key 使用del删除序列中的元素时 None 对于不可删除的序列或映射,无需实现该函数,其他同__getitem__
__contains__ self, item 使用in作为运算符时 bool 对于映射类型,__contains__接收的item参数对应映射的key部分而不是value部分。若没有定义该函数,Python首先通过__iter__进行查找,其次使用__getitem__进行查找
__iter__ self 调用iter()函数或使用循环时 返回值应为可迭代对象,对于可迭代对象,__iter__函数返回自身
__reversed__ self 调用reversed()函数时 返回值应为可迭代对象,且是原序列的逆序
__next__ self 调用next()函数时 返回可迭代对象中的下一个元素,当到达最后一个元素时抛出StopIteration
__call__ self, *args, **kwargs 当对象以函数的方式被调用时 定义了__call__方法的对象属于typing.Callable类型
__enter__ self 使用with关键词声明上下文时 Any 返回值分配给as关键词指明的对象
__exit__ self, exc_type, exc_value, traceback 离开with块时 Any with块执行过程中没有出现异常,则exc_type, exc_value, traceback参数均为None,否则exit块可以对异常进行处理
__new__ cls[, ...] 创建类实例时 Any __new__为静态方法且先于__init__执行,__new__的其余参数会传递给__init__。若__new__返回的类型与创建的类型不对应,则不会执行__init__
__init__ self, ... 初始化类实例时 None 若基类定义了__init__,则在子类的__init__中必须有super().__init()的显式调用
__del__ self 对象销毁时 None 当对象的引用计数减为0时,对象才可能被删除
__getattribute__ self, name 使用self.name访问对象的属性时 Any 为避免无限递归,子类的__getattribute__方法的实现中必须包含父类的__getattribute__方法调用
__getattr__ self, name 使用self.name访问对象的不存在属性或__getattribute__函数抛出AttributeError异常 Any __getattribute__正常执行时__getattr__不会执行
__setattr__ self, name, value 使用self.name设置属性的值时 None 当试图通过__setattr__向对象设置属性值时必须通过类型的__setattr__方法进行设置
__delattr__ self, name 执行del self.name None 仅当del self.name语句有意义时,才需要实现该方法
__dir__ self 执行dir()函数时 Sequence dir()会自动进行排序处理

运算符重载相关的魔术方法:

描述 方法名 运算符
取负 __neg__ -
取正 __pos__ +
绝对值 __abs__ abs()
小于 __lt__ <
小于等于 __le__ <=
等于 __eq__ ==
不等于 __ne__ !=
大于 __gt__ >
大于等于 __ge__ >=
相加 __add__ +
相减 __sub__ -
相乘 __mul__ *
实数除 __truediv__ /
整数除 __floordiv__ //
取余 __mod__ %
整数除+取余 __divmod__ divmod()
乘方 __pow__ **
取整 __round__ round()
按位取反 __invert__ ~
左移 __lshift__ <<
右移 __rshift__ >>
按位与 __and__ &
按位或 __or__ `
按位异或 __xor__ ^