python字节码中的加载名称和加载全局有什么区别?

2023-12-30

load name 接受其参数,并将 store name 存储的名称值推入堆栈,该值由参数指示的位置存储。 load global 做了类似的事情,但字节码中似乎没有 store global。那么有什么区别以及如何加载全局工作


和...之间的不同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作者:艾莉森·卡普图尔。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

python字节码中的加载名称和加载全局有什么区别? 的相关文章

随机推荐