操作符重载,也是通过一些 dundle 函数来实现的。比如 __add__
, __radd__
等。操作符分为一元(unary)和二元(binary / infix),重载函数长得像:
class Vector:
def __neg__(self):
return Vector(-x for x in self)
def __add__(self, other):
pairs = itertools.zip_longest(self, other, fillvalue=0.0)
return Vector(a + b for a, b in pairs)
二元操作符,有正向反向之分,比如对于 +
,有:
Vector([1, 2]) + [3]
:调用了Vector.__add__
函数[3] + Vector([1, 2])
:调用Vector.__radd__
函数
关于 a+b
会怎么执行,这个图描述得很清楚:
同时二元操作符都有 in-place 版本,比如 __iadd__
,即 +=
操作符对应的函数。
写操作符重载函数很 trivial,需要写时具体看看书中建议。比较值得关注的是,如果二元操作符的两个值并不适合做操作(比如 'str' + [1, 2, 3]),你的相应 __add__
函数需要返回 NotImplemented
常量(而不是抛出 NotImplementedError
异常)。
对于用于比较的操作符,他们不需要专门的 reverse method,如下图:
如何写 __add__
和 __iadd__
,书中有个不错的例子:
import itertools
from tombola import Tombola
from bingo import BingoCage
class AddableBingoCage(BingoCage):
def __add__(self, other):
if isinstance(other, Tombola):
return AddableBingoCage(self.inspect() + other.inspect())
else:
return NotImplemented
def __iadd__(self, other):
if isinstance(other, Tombola):
other_iterable = other.inspect()
else:
try:
other_iterable = iter(other)
except TypeError:
self_cls = type(self).__name__
msg = "right operand in += must be {!r} or an iterable"
raise TypeError(msg.format(self_cls))
self.load(other_iterable)
return self
作者总结了下:
__add__
: The result is produced by calling the constructor AddableBingoCage to build a new instance.__iadd__
: The result is produced by returning self, after it has been modified.
Future Reading 和 Soapbox 部分,大多数内容涉及到不同语言对于操作符重载的选择和讨论。比如 Java 和 Go 就不支持操作符重载。Java 作者觉得 C++ 里操作符重载被滥用了,写了一堆不可理解的(not sensible)代码出来,它宁愿牺牲掉这块特性。