# 九、求值循环

到本章为止，你已经了解了如何将 Python 代码解析为抽象语法树并将其编译成 code object，这些 code object 包含了以字节码形式表示且相互独立的一系列操作。但要执行 code object 还缺少一项关键的内容，它们需要输入。在 Python 中，输入可能以局部变量或全局变量的形式出现。在本章中，你将接触到一个名为**值栈（value stack）** 的概念。在你编译的 code object 中，字节码操作会在值栈创建、修改并使用变量。

CPython 中执行代码的动作发生在一个核心循环中，这个循环又被称为**求值循环 (evaluation loop)**。CPython 解释器将在该循环中解析并执行由序列化的 `.pyc` 文件或由编译器得到的 code object：

图字翻译 Parser：解析器 AST：抽象语法树 Compiler：编译器 CFG：控制流图 Assembler：汇编器 Bytecode：字节码 Execution：执行

![图9.1 The Evaluation Loop](https://1029588898-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FJhewUmzI3BNeGgeFH9Rv%2Fuploads%2Fgit-blob-85dc576031c2d9c48ca856403cbd125eca688c3d%2F%E5%9B%BE9.1%20The%20Evaluation%20Loop.png?alt=media)

在求值循环中，每一个字节码指令都是使用基于系统的 “[栈帧](http://www.cs.uwm.edu/classes/cs315/Bacon/Lecture/HTML/ch10s07.html)” 获取和执行的。

{% hint style="info" %}
**注**

不只 Python 中，许多运行时都使用了\*\*栈帧（stack frame）\*\*这种数据结构。栈帧保证了可以调用函数并获取返回值，它包含了参数、局部变量和其他一些状态信息。

每次调用函数时都会创建一个栈帧，这些栈帧按调用顺序堆叠在一起。如果抛出了未处理的异常，你就可以看到 CPython 的帧栈信息：

```python
Traceback (most recent call last):
    File "example_stack.py", line 8, in <module> <--- Frame
        function1()
    File "example_stack.py", line 5, in function1 <--- Frame
        function2()
    File "example_stack.py", line 2, in function2 <--- Frame
        raise RuntimeError
    RuntimeError
```

{% endhint %}

### 相关的源文件

以下是与求值循环相关的源文件：

| 文件                 | 功能           |
| ------------------ | ------------ |
| Python/ceval.c     | 实现求值循环的核心代码  |
| Python/ceval-gil.h | GIL 的定义和控制算法 |

### 重要术语

以下是你将在本章中使用的几个重要术语：

* 求值循环将会获取一个 **code object** 并将其转换为一系列的 **frame object**；
* 解释器至少需要一个**线程**；
* 每个线程都有自己的**线程状态（thread state）**；
* frame object 在被称为\*\*帧栈（frame stack）\*\*的栈中执行；
* 变量在**值栈**中被引用。
