load name takes its argument and pushes onto the stack the value of the name stored by store name at the position indicated by the argument . load global does something similar, but there appears to be no store global in the bytecode. So what is the difference and how does load global work
The difference between LOAD_NAME
and LOAD_GLOBAL
is where they search for a given name
.
LOAD_NAME
When Python encounters a LOAD_NAME
opcode:
- It first searches in
f_locals
- the names local to the current frame object.
- If it doesn't find the given name in
f_locals
, it then proceeds to search in f_globals
- the global names of the frame object. These are the names in the surrounding scope of the frame object.
- If doesn't find the name in
f_globals
, it then searches f_builtins
. f_builtins
is a dictionary of the builtin names Python uses.
- If all of the above fails, Python raises a
NameError
.
Here the relevant C code where the virtual machine executes a LOAD_NAME
instruction:
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
When Python encounters a LOAD_GLOBAL
opcode:
- Python first searches for the name in
f_globals
- the names in surrounding scopes that the current frame object references.
- If doesn't find the name in
f_globals
, it then searches f_builtins
. f_builtins
is a dictionary of the builtin names Python uses.
- If all of the above fails, Python raises a
NameError
.
Here the relevant C code where the virtual machine executes a LOAD_GLOBAL
instruction:
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();}
So, what's the difference?
As you could probably see, the difference is that LOAD_GLOBAL
skips straight to searching the global names of the frame object, whereas LOAD_NAME
starts searching the local names and makes it way up. The LOAD_GLOBAL
opcode is useful for the case where Python already knows that a name cannot be local, so it skips searching local names altogether.
Note: if your looking to learn more about how the Python virtual machine works, I looking over Byterun, a pure-python implementation of the CPython virtual machine. It also has an accompanying article by Allison Kaptur.