原题 | Unravelling binary arithmetic operations in Python
作者 | Brett Cannon
译者 | 豌豆花下猫
来源| Python猫
声明 | 本翻译是出于交流学习的目的,基于 CC BY-NC-SA 4.0 授权协议。为便于阅读,内容略有改动。
大家对我解读属性访问的博客文章反应热烈,这启发了我再写一篇关于 Python 有多少语法实际上只是语法糖的文章。在本文中,我想谈谈二元算术运算。
具体来说,我想解读减法的工作原理:a - b。我故意选择了减法,因为它是不可交换的。这可以强调出操作顺序的重要性,与加法操作相比,你可能会在实现时误将 a 和 b 翻转,但还是得到相同的结果。
查看 C 代码
按照惯例,我们从查看 CPython 解释器编译的字节码开始。
>>> def sub(): a - b
...
>>> import dis
>>> dis.dis(sub)
1 0 LOAD_GLOBAL 0 (a)
2 LOAD_GLOBAL 1 (b)
4 BINARY_SUBTRACT
6 POP_TOP
8 LOAD_CONST 0 (None)
10 RETURN_VALUE
看起来我们需要深入研究 BINARY_SUBTRACT 操作码。翻查 Python/ceval.c 文件,可以看到实现该操作码的 C 代码如下:
case TARGET(BINARY_SUBTRACT): {
PyObject *right = POP();
PyObject *left = TOP();
PyObject *diff = PyNumber_Subtract(left, right);
Py_DECREF(right);
Py_DECREF(left);
SET_TOP(diff);
if (diff == NULL)
goto error;
DISPATCH();
}
这里的关键代码是PyNumber_Subtract(),实现了减法的实际语义。继续查看该函数的一些宏,可以找到binary_op1() 函数。它提供了一种管理二元操作的通用方法。
不过,我们不把它作为实现的参考,而是要用Python的数据模型,官方文档很好,清楚介绍了减法所使用的语义。
从数据模型中学习
通读数据模型的文档,你会发现在实现减法时,有两个方法起到了关键作用:__sub__ 和 __rsub__。
1、__sub__()方法
当执行a - b 时,会在 a 的类型中查找__sub__(),然后把 b 作为它的参数。这很像我写属性访问的文章 里的__getattribute__(),特殊/魔术方法是根据对象的类型来解析的,并不是出于性能目的而解析对象本身;在下面的示例代码中,我使用_mro_getattr() 表示此过程。
因此,如果已定义 __sub__(),则 type(a).__sub__(a,b) 会被用来作减法操作。(译注:魔术方法属于对象的类型,不属于对象)
这意味着在本质上,减法只是一个方法调用!你也可以将它理解成标准库中的 operator.sub() 函数。
我们将仿造