读 Python behind the scenes #5: how variables are implemented in CPython
https://tenthousandmeters.com/blog/python-behind-the-scenes-5-how-variables-are-implemented-in-cpython/
通过这一章我们将尝试理解,下面这一行简短的 python 代码背后,究竟发生了什么?
a = b
初探字节码
首先将代码片段转化为字节码:
$ echo 'a = b' | python3.9 -m dis
1 0 LOAD_NAME 0 (b)
2 STORE_NAME 1 (a)
...
从之前的章节了解到,CPython VM 通过 value stack 执行计算:
- 第一步:
LOAD_NAME获取b变量的值,并压入 stack 中 - 第二步:
STORE_NAME从 stack 中弹出值,并赋值给a
STORE_NAME
上一章的结尾提到,每个字节码对应 Python/ceval.c 大循环中的一个 switch 语句,所以我们可通过该方法快速查看 STORE_NAME 对应的源码实现:
// "2 STORE_NAME 1 (a)"
case TARGET(STORE_NAME): {
// 1. 通过字节码的参数 oparg(index 位置),获取变量名(string 对象)
PyObject *name = GETITEM(names, oparg);
// 2. 从 value stack 中 pop 一个值(python 对象的引用)
PyObject *v = POP();
PyObject *ns = f->f_locals;
int err;
if (ns == NULL) {
_PyErr_Format(tstate, PyExc_SystemError,
"no locals found when storing %R", name);
Py_DECREF(v);
goto error;
}
// 3. 关联 `*name` & `*v`
// P.S. 通过 frame object 中的 f_locals 变量维护 mapping 关系
if (PyDict_CheckExact(ns))
err = PyDict_SetItem(ns, name, v);
else
err = PyObject_SetItem(ns, name, v);
Py_DECREF(v);
if (err != 0)
goto error;
DISPATCH();
}
LOAD_NAME
LOAD_NAME 相对更加复杂一些,因为 VM 会从 f_locals 以及多个位置获取变量的值:
评论
?
参与讨论