和...之间的不同LOAD_NAME
and LOAD_GLOBAL
是他们搜索给定的name
.
LOAD_NAME
当Python遇到一个LOAD_NAME
opcode:
- 它首先搜索
f_locals
- 当前框架对象的本地名称。
- 如果在中找不到给定的名称
f_locals
,然后继续搜索f_globals
- 框架对象的全局名称。这些是框架对象周围范围内的名称。
- 如果没有找到该名称
f_globals
,然后它会搜索f_builtins
. f_builtins
是 Python 使用的内置名称的字典。
- 如果以上所有方法都失败,Python 会抛出一个错误
NameError
.
这里是虚拟机执行的相关C代码LOAD_NAME
操作说明:
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
opcode:
- Python首先在中搜索名称
f_globals
- 当前框架对象引用的周围范围中的名称。
- 如果没有找到该名称
f_globals
,然后它会搜索f_builtins
. f_builtins
是 Python 使用的内置名称的字典。
- 如果以上所有方法都失败,Python 会抛出一个错误
NameError
.
这里是虚拟机执行的相关C代码LOAD_GLOBAL
操作说明:
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 已经知道名称不能是本地名称的情况很有用,因此它会完全跳过搜索本地名称。
Note:如果您想了解有关 Python 虚拟机如何工作的更多信息,我会查看Byterun https://github.com/nedbat/byterun/blob/master/byterun/pyvm2.py,CPython 虚拟机的纯 python 实现。它还有一个附带文章 http://www.aosabook.org/en/500L/a-python-interpreter-written-in-python.html作者:艾莉森·卡普图尔。