Skip to content

Commit 50dc75b

Browse files
committed
为「一般表达式与名字空间」补图
新增 LOAD_FAST 下标取值、LOAD_NAME 运行期字典查找、BUILD_LIST 打包三张图
1 parent 4862f58 commit 50dc75b

4 files changed

Lines changed: 156 additions & 0 deletions

File tree

Lines changed: 43 additions & 0 deletions
Loading
Lines changed: 47 additions & 0 deletions
Loading

vm/expressions-and-names/index.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ Py_INCREF(value);
4848
PUSH(value); // 压入求值栈
4949
```
5050
51+
![LOAD_FAST 按下标取局部变量](fastlocals.svg)
52+
5153
这是 Python 里取值最快的路径——一次数组索引而已。这也解释了一个常见报错的由来:如果这个槽位还没被赋值就被读取,`GETLOCAL` 取到 `NULL`,于是抛 **`UnboundLocalError`**:
5254
5355
```python
@@ -119,6 +121,8 @@ if (v == NULL) {
119121
}
120122
```
121123

124+
![LOAD_NAME 运行期依次查三个名字空间](name-lookup.svg)
125+
122126
可以看到 `LOAD_NAME``LOAD_GLOBAL` 多查一层局部、比 `LOAD_FAST` 多了字典查找——它最灵活,但也最慢。这正是为什么**函数内部要尽量用局部变量**:函数体里的名字能走 `LOAD_FAST` 的快路径,而模块顶层只能用 `LOAD_NAME`
123127

124128
## LEGB:四类作用域与四条指令
@@ -181,6 +185,8 @@ while (--oparg >= 0) {
181185
PUSH(list); // 列表压回栈顶
182186
```
183187

188+
![BUILD_LIST 把栈顶值打包成列表](build-list.svg)
189+
184190
`BUILD_TUPLE``BUILD_MAP`(字典)、`COMPARE_OP`(比较)……全是这个模式:操作数已经在栈上备好,指令弹出它们、算出结果、压回栈顶。理解了「**压操作数 → 指令计算 → 压回结果**」这一条,绝大多数表达式字节码都能照着读下来。
185191

186192
---
Lines changed: 60 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)