特殊方法的存在是为了被python解释器调用的,你自己并不需要调用他们。也就是说没有 my_object.len() 这种写法,而应该使用 len(my_object)。
在执行 len(my_object) 的时候,如果 my_object 是一个自定义类的对象,那么Python 会自己去调用其中由你实现的 len 方法。
'''
通常你的代码无需直接使用特殊方法。除非有大量的元编程存在,直接调用特殊方法的频率应该远远低于你去实现它们的次数。
唯一的例外可能是 __init__ 方法,你的代码里可能经常会用到它,目的是在你自己的子类的 __init__ 方法中调用超类的构造器
'''
一个简单的二维向量类
from math import hypot
class Vector:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __repr__(self):
return 'Vector({}, {})'.format(self.x, self.y)
def __abs__(self):
return hypot(self.x, self.y)
def __bool__(self):
return bool(self.x or self.y)
def __add__(self, other):
x = self.x + other.x
y = self.y + other.y
return Vector(x, y)
def __mul__(self, other):
return Vector(self.x * other, self.y * other)
虽然代码里有 6 个特殊方法,但这些方法(除了 init)并不会在这个类自身的代码中使用。即便其他程序要使用这个类的这些方法,也不会直接调用它们,一般只有 Python 的解释器会频繁地直接调用这些方法。接下来看看每个特殊方法的实现。
字符串表现形式
def __repr__(self):
"""
__repr__: 类似于len, repr把一个对象用字符串的形式表达出来以便辨认。
repr通过__repr__这个特殊方法来得到一个对象的字符串表现形式,如果没有实现该特殊方法,输出结果为如下形式:
<__main__.Vector object at 0x000002722A5B6610>
__repr__ 所返回的字符串应该准确、无歧义,并且尽可能表达出如何用代码创建出这个被打印的对象。
因此这里使用了类似调用对象构造器的表达形式
__repr__ 和 __str__ 的区别在于,后者是在 str() 函数被使用,
或是在用 print 函数打印一个对象的时候才被调用的,并且它返回的字符串对终端用户更友好
如果你只想实现这两个特殊方法中的一个,__repr__ 是更好的选择
"""
return 'Vector({}, {})'.format(self.x, self.y)
vector = Vector(1, 2)
print(vector) # Vector(1, 2)
算术运算符
'''
通过 __add__ 和 __mul__,示例向量类带来了 + 和 * 这两个算术运算符
'''
if __name__ == '__main__':
vector1 = Vector(1, 2)
vector2 = Vector(1, 4)
print(vector1 + vector2) # Vector(2, 6)
print(vector1 * 3) # Vector(3, 6)
自定义布尔值
'''
默认情况下,我们自己定义的类的实例总被认为是真的,除非这个类对__bool__ 或者 __len__ 函数有自己的实现。
bool(x) 的背后是调用x.__bool__() 的结果;
如果不存在 __bool__ 方法,那么 bool(x)会尝试调用 x.__len__()。
若返回 0,则 bool 会返回 False;否则返回 True
我们对 __bool__ 的实现很简单,如果一个向量的模是 0,那么就返回False,其他情况则返回 True。
因为 __bool__ 函数的返回类型应该是布尔型,所以我们通过 bool(abs(self)) 把模值变成了布尔值
'''
def __bool__(self):
return bool(self.x or self.y)
通过实现特殊方法,自定义数据类型可以表现得跟内置类型一样,从而让我们写出更具表达力的代码——或者说,更具 Python 风格的代码