> For the complete documentation index, see [llms.txt](https://hai-shi.gitbook.io/cpython-internals/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://hai-shi.gitbook.io/cpython-internals/8-compiler/8.10-example.md).

# 8.10 一个示例：实现约等于操作符

在学习了编译器、字节代码指令和汇编器之后，你现在可以修改 CPython 以支持在上一章中编译到语法中的约等于运算符。

首先，你必须为 `Py_AlE` 运算符添加一个内部 `#define`，以便 `PyObject` 的富比较函数可以引用到它。

打开 `Include/object.h` 并找到以下 `#define` 语句：

代码不翻译

添加一个附加值 `PyAlE`，值为 6：

```c
/* New almost-equal comparator */
#define Py_AlE 6
```

就在此表达式的下面是一个宏：`Py_RETURN_RICHCOMPARE`。使用 `Py_AlE` 的 case 语句更新此宏。

代码不翻译

在 `Object/object.c` 中，有一个保护程序来检查运算符是否在 0 和 5 之间。因为你添加了值 6，所以必须更新此断言：

代码不翻译

将最后一行更改为以下行：

```c
assert(Py_LT <= op && op <= Py_AlE);
```

接下来，你需要更新 `COMPARE_OP` 操作码以支持 `Py_AlE` 作为运算符类型值。

首先，编辑 `Object/object.c` 并将 `Py_AlE` 添加到 `_Py_SwapedOp` 列表中。此列表用于匹配自定义类在方法下是否有一个运算符魔法方法，而不是其他方法。

例如，如果你定义了一个类：`Coordinate`，你可以通过实现 `__eq__` 魔术方法来定义不等式运算符：

代码不翻译

即使你还没有为 `Coordinate` 实现 `__ne__`（不等于）, CPython 假设可以使用 `__eq__` 的相反内容。

```python
>>> Coordinate(1, 100) != Coordinate(2, 400)
True
```

在 `Object/object.c` 中，找到 `_Py_SwapedOp` 列表并将 `Py_AlE` 添加到末尾。然后在 `opstrings` 列表的末尾添加 “\~=”。

代码不翻译

打开 `Lib/opcode.py` 并编辑富比较运算符列表：

```c
cmp_op = ('<', '<=', '==', '!=', '>', '>=')
```

在元组末尾添加新运算符：

```c
cmp_op = ('<', '<=', '==', '!=', '>', '>=', '~=')
```

如果没有在类上实现富比较运算符，则抛出的错误消息会使用到 `opstrings` 列表。

接下来，你可以更新编译器以处理 `BinOp` 节点中 `PyCmp_AlE` 属性的情况。打开 `Python/compile.c` 并查找到 `compiler_addcompare()`：

代码不翻译

接下来，在此 switch 语句中添加另一个 `case`，以将 AST 内的 `comp_op` 枚举中的 `AlE` 与 `PyCmp_AlE` 比较操作码枚举进行配对：

```c
...
    case AlE:
        cmp = Py_AlE;
        break;
```

现在，你可以根据以下场景对约等于运算符的行为进行编程：

* 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`。你将在下一章中学习到求值循环。

搜索此代码段：

```c
...
    case TARGET(COMPARE_OP): {
        assert(oparg <= Py_GE);
```

将断言更改为以下内容：

```c
assert(oparg <= Py_AlE);
```

重新编译 CPython 后，打开一个交互式解释器进行测试：

```bash
$ ./python
>>> 1.0 ~= 1.01
True
>>> 1.02 ~= 1.01
True
>>> 1.02 ~= 2.01
False
>>> 1 ~= 1.01
True
>>> 1 ~= 1
True
>>> 1 ~= 2
False
>>> 1 ~= 1.9
False
>>> 1 ~= 2.0
False
>>> 1.1 ~= 1.101
True
```

在后面的章节中，你将把这个实现扩展到其他类型。
