load name命令需要一个参数,并将store name所存储的名称对应位置的值推入堆栈。load global命令也类似,但是字节码中没有store global命令。那么它们之间有什么区别,load global又是如何工作的呢?
load name命令需要一个参数,并将store name所存储的名称对应位置的值推入堆栈。load global命令也类似,但是字节码中没有store global命令。那么它们之间有什么区别,load global又是如何工作的呢?
LOAD_NAME
和 LOAD_GLOBAL
的区别在于它们搜索给定 name
的位置。
LOAD_NAME
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
从本地名称开始搜索并向上移动。如果Python已知某个名称不能是本地名称,则LOAD_GLOBAL
操作码非常有用,因此完全跳过搜索本地名称。
注意:如果您想了解有关Python虚拟机如何工作的更多信息,请查看Byterun,这是CPython虚拟机的纯Python实现。 Allison Kaptur还撰写了一篇相关文章。
有一个名为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
/STORE_GLOBAL
是指令,当locals() != globals()
并且Python在编译时(使用SymbolTable)知道它需要完全跳过locals字典时,它才有意义。这只适用于函数内部。locals() == globals()
,因此编译器遵循正常的查找规则,使用LOAD_NAME
(除非您直接使用global
,请参见dis('global b; b = 40')
)。