如何使用特殊的方法
特殊方法需要了解的第一件事是,他们注定要被Python解释器调用,而不是被您调用。您不写my_object.__len__()
。您应该写len(my_object)
,如果my_object是用户定义的类的一个实例,然后Python调用您实现的__len__
实例方法。
但对于内置类型如表,字符串,字节数组等,解释器采用了一个便捷方法:CPython实现len()
方法实际上返回的是在PyVarObject
C结构体中ob_size
字段的值,表示内存中的任何变量大小的内置对象。这比调用一个方法要快得多。
更多的并不是如此,特殊方法的调用往往是隐式的。好比说,for i in x
这句话实际上会调用iter(x)
这反过来又可能会调用x.__iter__()
,如果它是可用的。
通常情况下,您的代码不应该有许多直接调用特殊方法的调用。除非您是做了很多的元编程,您应该实现特殊的方法而不是显式调用它们。唯一用户代码经常直接调用的方法是是__init__
,在您自己的__init__
中实现调用父类的初始化。
如果您需要调用一个特殊的方法,通常更好的是调用相关的内置功能,如len, iter, str
等。
这些内置功能调用相应的特殊方法,但往往提供其他服务,同时,内置类型速度比的方法调用的快。
避免使用__foo__
语法创建任意自定义属性,因为这样的名字可能在未来获得特殊的意义,即使它们现在是未使用的。
模拟数值类型 一些特殊的方法允许用户对象响应算子如 + 。我们将在第13章更详细的介绍,但在这里,我们的目标是通过另一个简单的例子进一步说明特殊方法的用法。 我们将实现一个类来表示2维向量,即如在数学和物理中使用的欧氏向量(见下图)
### tips: 内置的complex类型可以用来表示二维向量,但是我们类可以推广到表示n维向量。在第14章中,我们将这样做。我们将开始设计API通过为一个类写一个模拟控制台会话,后面我们可以用作doctest。下面的代码片段测试向量加法的照片(见上图)
>>> v1 = Vector(2, 4)
>>> v2 = Vector(2, 1)
>>> v1+v2
Vector(4, 5)
注意+操作符如何产生一个向量结果并以一个友好的方式在控制台显示
内置函数abs
返回的整数和浮点数的绝对值,和复数的模,所以为保持一致,我们的API也使用了abs
来计算一个向量的大小
>>> v = Vector(3, 4)
>>> abs(v)
5.0
我们还可以实现*操作符来执行向量乘法,即由一个数字乘以一个向量,以产生一个与原向量相同的方向和乘以原向量模的新的向量:
>>>v*3
Vector(9, 12)
>>> abs(v * 3)
15.0
例1-2是一个向量类,通过特殊的方法__repr__,__abs__,__add__和__mul__
的使用,实现所描述的操作符
from math import hypot
class Vector:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __repr__(self):
return 'Vector(%r, %r)' % (self.x, self.y)
def __abs__(self):
return hypot(self.x, self.y)
def __bool__(self):
return bool(abs(self))
def __add__(self, other):
x = self.x + other.x
y = self.y + other.y
return Vector(x, y)
def __mul__(self, scalar):
return Vector(self.x * scalar, self.y * scalar)
请注意,虽然我们实施了四个特殊的方法(除了__init__
),它们中没有一个是类直接调用、或是在控制台列表中说明的类的典型用法中直接调用的。如前所述,Python解释器是调用特殊方法唯一的常客。在下一节中,我们讨论了每种特殊方法的代码。