8.10 一个示例:实现约等于操作符
在学习了编译器、字节代码指令和汇编器之后,你现在可以修改 CPython 以支持在上一章中编译到语法中的约等于运算符。
首先,你必须为 Py_AlE
运算符添加一个内部 #define
,以便 PyObject
的富比较函数可以引用到它。
打开 Include/object.h
并找到以下 #define
语句:
代码不翻译
添加一个附加值 PyAlE
,值为 6:
就在此表达式的下面是一个宏:Py_RETURN_RICHCOMPARE
。使用 Py_AlE
的 case 语句更新此宏。
代码不翻译
在 Object/object.c
中,有一个保护程序来检查运算符是否在 0 和 5 之间。因为你添加了值 6,所以必须更新此断言:
代码不翻译
将最后一行更改为以下行:
接下来,你需要更新 COMPARE_OP
操作码以支持 Py_AlE
作为运算符类型值。
首先,编辑 Object/object.c
并将 Py_AlE
添加到 _Py_SwapedOp
列表中。此列表用于匹配自定义类在方法下是否有一个运算符魔法方法,而不是其他方法。
例如,如果你定义了一个类:Coordinate
,你可以通过实现 __eq__
魔术方法来定义不等式运算符:
代码不翻译
即使你还没有为 Coordinate
实现 __ne__
(不等于), CPython 假设可以使用 __eq__
的相反内容。
在 Object/object.c
中,找到 _Py_SwapedOp
列表并将 Py_AlE
添加到末尾。然后在 opstrings
列表的末尾添加 “~=”。
代码不翻译
打开 Lib/opcode.py
并编辑富比较运算符列表:
在元组末尾添加新运算符:
如果没有在类上实现富比较运算符,则抛出的错误消息会使用到 opstrings
列表。
接下来,你可以更新编译器以处理 BinOp
节点中 PyCmp_AlE
属性的情况。打开 Python/compile.c
并查找到 compiler_addcompare()
:
代码不翻译
接下来,在此 switch 语句中添加另一个 case
,以将 AST 内的 comp_op
枚举中的 AlE
与 PyCmp_AlE
比较操作码枚举进行配对:
现在,你可以根据以下场景对约等于运算符的行为进行编程:
1~=2 为假;
1~=1.01 使用向下取整时为真。
你可以通过一些额外的代码来实现这一点。现在,你将把两个浮点数转换为整数并进行比较。
CPython 的 API 有许多用于处理 PyLong(int)
和 PyFloat(float)
类型的函数。“对象和类型”章节会介绍此内容。
在 Objects/floatobject.c
中找到 float_richcompare()
,然后在 Compare: goto
定义下添加以下示例代码:
代码不翻译
当使用了约等于运算符时,此代码将处理浮点数的比较。它使用的逻辑类似于 PEP 485 中定义的tomath.isclose()
,但硬编码的绝对误差为 0.1。
你需要更改的另一个保护程序是在求值循环中:Python/ceval.c
。你将在下一章中学习到求值循环。
搜索此代码段:
将断言更改为以下内容:
重新编译 CPython 后,打开一个交互式解释器进行测试:
在后面的章节中,你将把这个实现扩展到其他类型。
Last updated