load name接受其参数,并将商店名称存储的名称的值推送到堆栈,该位置由参数指示。 load global做了类似的事情,但字节码中似乎没有全局存储。那么有什么不同以及如何加载全局工作
LOAD_NAME
和LOAD_GLOBAL
之间的区别在于他们搜索给定的name
。
LOAD_NAME
当Python遇到LOAD_NAME
操作码时:
f_locals
中搜索 - 当前帧对象的本地名称。f_locals
中找不到给定的名称,则它继续搜索f_globals
- 框架对象的全局名称。这些是框架对象的周围范围中的名称。f_globals
中找不到该名称,则会搜索f_builtins
。 f_builtins
是Python使用的内置名称的字典。NameError
。这里是虚拟机执行LOAD_NAME
指令的相关C代码:
TARGET(LOAD_NAME) {
PyObject *name = GETITEM(names, oparg);
PyObject *locals = f->f_locals;
PyObject *v;
if (locals == NULL) {
PyErr_Format(PyExc_SystemError,
"no locals when loading %R", name);
goto error;
}
if (PyDict_CheckExact(locals)) {
v = PyDict_GetItem(locals, name);
Py_XINCREF(v);
}
else {
v = PyObject_GetItem(locals, name);
if (v == NULL) {
if (!PyErr_ExceptionMatches(PyExc_KeyError))
goto error;
PyErr_Clear();
}
}
if (v == NULL) {
v = PyDict_GetItem(f->f_globals, name);
Py_XINCREF(v);
if (v == NULL) {
if (PyDict_CheckExact(f->f_builtins)) {
v = PyDict_GetItem(f->f_builtins, name);
if (v == NULL) {
format_exc_check_arg(
PyExc_NameError,
NAME_ERROR_MSG, name);
goto error;
}
Py_INCREF(v);
}
else {
v = PyObject_GetItem(f->f_builtins, name);
if (v == NULL) {
if (PyErr_ExceptionMatches(PyExc_KeyError))
format_exc_check_arg(
PyExc_NameError,
NAME_ERROR_MSG, name);
goto error;
}
}
}
}
PUSH(v);
DISPATCH();
}
LOAD_GLOBAL
当Python遇到LOAD_GLOBAL
操作码时:
f_globals
中搜索名称 - 当前帧对象引用的周围范围中的名称。f_globals
中找不到该名称,则会搜索f_builtins
。 f_builtins
是Python使用的内置名称的字典。NameError
。这里是虚拟机执行LOAD_GLOBAL
指令的相关C代码:
TARGET(LOAD_GLOBAL) {
PyObject *name = GETITEM(names, oparg);
PyObject *v;
if (PyDict_CheckExact(f->f_globals)
&& PyDict_CheckExact(f->f_builtins))
{
v = _PyDict_LoadGlobal((PyDictObject *)f->f_globals,
(PyDictObject *)f->f_builtins,
name);
if (v == NULL) {
if (!_PyErr_OCCURRED()) {
/* _PyDict_LoadGlobal() returns NULL without raising
* an exception if the key doesn't exist */
format_exc_check_arg(PyExc_NameError,
NAME_ERROR_MSG, name);
}
goto error;
}
Py_INCREF(v);
}
else {
/* Slow-path if globals or builtins is not a dict */
/* namespace 1: globals */
v = PyObject_GetItem(f->f_globals, name);
if (v == NULL) {
if (!PyErr_ExceptionMatches(PyExc_KeyError))
goto error;
PyErr_Clear();
/* namespace 2: builtins */
v = PyObject_GetItem(f->f_builtins, name);
if (v == NULL) {
if (PyErr_ExceptionMatches(PyExc_KeyError))
format_exc_check_arg(
PyExc_NameError,
NAME_ERROR_MSG, name);
goto error;
}
}
}
PUSH(v);
DISPATCH();
}
正如您可能看到的那样,区别在于LOAD_GLOBAL
直接跳过搜索框架对象的全局名称,而LOAD_NAME
开始搜索本地名称并使其向上移动。 LOAD_GLOBAL
操作码对于Python已经知道名称不能是本地的情况很有用,因此它会完全跳过搜索本地名称。
注意:如果您希望了解有关Python虚拟机如何工作的更多信息,请查看Byterun,它是CPython虚拟机的纯python实现。它还有Allison Kaptur的accompanying article。
有一个STORE_GLOBAL
。使用global
指令触发它并分配给该名称:
def foo():
global a
a = 3
dis(foo)
3 0 LOAD_CONST 1 (3)
2 STORE_GLOBAL 0 (a)
4 LOAD_CONST 0 (None)
6 RETURN_VALUE
当LOAD_GLOBAL
和Python知道(通过在编译期间使用SymbolTable)它需要完全跳过本地dict时,STORE_GLOBAL
/ locals() != globals()
是afaik的指令。这在函数内部是正确的。
在顶级范围,locals() == globals()
所以编译器遵循正常查找规则使用LOAD_NAME
(除非你直接使用global
,请参阅dis('global b; b = 40')
)。