LOAD_NAME和{}之间的区别在于它们搜索给定的name。在
LOAD_NAME
当Python遇到LOAD_NAME操作码时:它首先在f_locals-当前帧对象的本地名称中搜索。在
如果它在f_locals中找不到给定的名称,那么它将继续在f_globals中搜索框架对象的全局名称。这些是框架对象周围范围内的名称。在
如果在f_globals中找不到该名称,那么它将搜索f_builtins。f_builtins是Python使用的内置名称的字典。在
如果以上所有操作都失败,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操作码时:Python首先在f_globals中搜索名称,即当前帧对象引用的周围范围中的名称。在
如果在f_globals中找不到该名称,那么它将搜索f_builtins。f_builtins是Python使用的内置名称的字典。在
{12>如果上面的所有cda}都失败了。在
这里是虚拟机执行LOAD_GLOBAL指令的相关C代码:
^{pr2}$
那么,有什么区别?在
正如您可能看到的,不同之处在于LOAD_GLOBAL直接跳过搜索框架对象的全局名称,而LOAD_NAME则开始搜索本地名称,并向上搜索。LOAD_GLOBAL操作码对于Python已经知道名称不能是本地名称的情况非常有用,因此它完全跳过了对本地名称的搜索。在
注意:如果您想了解更多关于Python虚拟机如何工作的信息,我将查看Byterun,它是CPython虚拟机的纯Python实现。它也有一个accompanying article由艾莉森·卡普特创作。在