13.1 运算符重载基础
- Python 的运算符重载限制:不能重载内置类型运算符、只能重载现有的某些运算符。
13.2 一元运算符
- 常见的一元运算符:取负
-, __neg__、取正 +, __pos__、按位取反 ~, __invert__、绝对值 abs(), __abs__;
- 运算符的基本规则:始终返回一个新对象;
x 和 +x 不一定相等,如 decimal 模块中不同的上下文精度下会计算得到不同的数据。
13.3 重载加法运算符 +
- Python 为中缀运算符特殊方法提供了特殊的分派机制。对于
a + b:
- 如果
a 有 __add__ 方法且不返回 NotImplemented,则调用它;
- 如果
b 有 __radd__ 方法(反向特殊方法)且不返回 NotImplemented,则调用它;
- 否则抛出
TypeError;
NotImplemented 不是异常,是特殊的返回值。NotImplementedError 是异常,用于提示抽象基类的方法没有实现;
- 在实现运算符的过程中,为了遵守鸭子类型精神,通过捕获异常的方式来判断操作数类型,而非类型检测。且在无法处理计算时,需要返回
NotImplement,而非抛出异常。
13.4 重载标量乘法运算符 *
- 需要对数字的类型(如实复数)进行判断时,使用白鹅类型进行判断更合理、更灵活;
- Python 3.5 新引入了运算符
@,支持矩阵的点积(在 NumPy 库中有实现),特殊方法对应 __matmul__。
13.5 众多比较运算符
- 比较运算符和前面的中缀运算符的处理略有区别:
- 正向和反向调用会使用数学意义的反向运算,无反向特殊方法;
== 和 != 在反向调用失败后,会继续比较实例的 ID,以此为结果返回;
!= 运算符的后备机制为 == 运算结果的取反,无需单独实现。
13.6 增量赋值运算符
- 增量赋值不会修改不可变目标(不可变类型一定不能实现就地特殊方法),而是新建实例,并重新绑定变量;
- 如果实现了就地运算符方法(
__iadd__ 等),计算时会调用就地运算符方法,不创建新对象,必须返回自己 self;
- 不能盲目实现反向特殊方法:交换律可能不满足,需要考虑运算符本身逻辑。一般来说,如果正向方法只处理同类型操作数,则不需要实现反向方法,反向方法是为了处理类型不同的操作数;
- 小技巧:
- 列表的
+ 运算只能将两个列表加到一起,而 += 运算可以操作任何可迭代对象,其规则也适用于 .extend() 方法。+ 操作可接受的类型应当更严格;
- 导入标准库的语句要放在导入自己编写的模块之前;(与 C++不同)
- 判断并利用对象的可迭代性时,可以用
iter(iterable) 创建迭代器,性能更佳。