A code object generated by Python compiler contains a tuple of constants used in the instructions (named co_consts
) and also a tuple containing names (named co_names
).
Why having two distinct lists? Wouldn't be simpler to just use co_consts
for names too?
Consider the following function.
def f(x):
x += n
return x * 4
Here x
is a local name, its value can change. 4
is a constant. Its value will never change. However, it's still an object and it's better to cache them rather than create a new object each time it's needed. Finally, n
is a global reference. The string "n"
is stored by the function so that it can be used as a key to retrieve n
from the function's global context.
>>> f.__code__.co_nlocals # just 1 (for x)
1
>>> f.__code__.co_consts
(None, 4)
>>> f.__code__.co_names
('n',)
>>> "n" in f.__globals__ and globals() is f.__globals__
True
The reason for keeping names and consts separate is for the purposes of introspection. The only real reason to merge the tuples would be memory efficiency, though this would only gain you one object and one pointer per function. Consider the following function.
def g():
return "s" * n
If the tuple containing consts was merged with the tuple containing names then you (not the VM) wouldn't be able to tell which values were for accessing globals and which were constants of the function.
I know this answer is like 11 months out of date but from my tinkering it seems the following is happening
To access co_names in bytecode, one uses LOAD_GLOBAL(co names index) and this pushes a reference to the desired co_names onto the stack, eg its indirect
To access co_consts in bytecode, one uses LOAD_CONST(co consts index) and this pushes the actual value stored at the desired co_consts onto the stack, eg its direct
I'm not sure it has any direct bearing at a python level, but at a bytecode level its a profound difference
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With