# 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
```

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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://hai-shi.gitbook.io/cpython-internals/8-compiler/8.10-example.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
