Skip to content

24. Constants, Names, and Locals

How compile-time constants are folded, names interned, and local variable slots assigned in the code object.

A code object does not store Python source code as text. It stores compact tables and bytecode instructions that refer to those tables by index.

Three of the most important tables are:

co_consts
co_names
co_varnames

They separate repeated data from the instruction stream.

For this function:

def f(a):
    b = len(a)
    return b + 1

CPython stores:

constants:
    None
    1

names:
    len

local variables:
    a
    b

The bytecode instructions then refer to those entries by index.

24.1 Position in the Compilation Pipeline

Constants, names, and locals are assembled during compilation.

AST
symbol table
compiler
instruction stream
constant table
name table
local variable table
code object

These tables are part of the code object.

The interpreter uses them when executing bytecode.

LOAD_CONST 1
LOAD_GLOBAL 0
LOAD_FAST 0
STORE_FAST 1

Each numeric operand indexes into one of the code object’s tables.

24.2 Why Code Objects Use Tables

Bytecode needs to be compact.

Instead of embedding full Python objects or strings directly inside each instruction, CPython stores them once in a table.

Example:

def f():
    print("hello")
    print("hello")

The string "hello" can appear once in co_consts.

The name "print" can appear once in co_names.

The bytecode references them by index.

Conceptually:

co_consts:
    0: None
    1: "hello"

co_names:
    0: "print"

bytecode:
    LOAD_GLOBAL 0       print
    LOAD_CONST 1        "hello"
    CALL
    POP_TOP

    LOAD_GLOBAL 0       print
    LOAD_CONST 1        "hello"
    CALL
    POP_TOP

This reduces duplication and gives the interpreter stable lookup positions.

24.3 Constants

Constants are literal values and compiled artifacts stored in co_consts.

Common constants include:

None
True
False
integers
floats
complex numbers
strings
bytes
tuples of constants
frozensets of constants
nested code objects

Example:

def f():
    return 123, "abc", None

Inspect:

print(f.__code__.co_consts)

Typical shape:

(None, 123, 'abc')

The tuple returned by the function may itself be constructed from constants, or it may be stored as a constant tuple if the compiler can safely fold it.

24.4 The None Constant

Most code objects include None in co_consts.

Example:

def f():
    x = 1

Even though the source has no explicit return None, the function returns None implicitly.

The compiler must be able to emit:

LOAD_CONST None
RETURN_VALUE

At module level, CPython also often emits a final RETURN_VALUE with None to finish execution.

24.5 Numeric Constants

Numeric literals usually appear in co_consts.

Example:

def f():
    return 10 + 20

The AST may contain 10 + 20, but the compiler may fold this into 30.

Inspect:

import dis

def f():
    return 10 + 20

print(f.__code__.co_consts)
dis.dis(f)

You may see only 30 as a loaded constant.

Constant folding is conservative. CPython can fold expressions that are safe and deterministic at compile time. It cannot fold expressions with runtime effects.

Example:

def f():
    return 10 + x

x is not known at compile time, so this cannot become a constant.

24.6 String and Bytes Constants

String and bytes literals appear in co_consts.

Example:

def f():
    return "hello", b"world"

Inspect:

print(f.__code__.co_consts)

Adjacent string literals may be concatenated during compilation:

def f():
    return "hello" "world"

This is a compile-time operation. It does not call string addition at runtime.

The result is one constant:

"helloworld"

24.7 Tuple Constants

Tuples containing only constants can be stored as constants.

Example:

def f():
    return (1, 2, 3)

The compiler may store the entire tuple:

(1, 2, 3)

in co_consts.

But if an element is dynamic:

def f(x):
    return (1, x, 3)

the tuple must be built at runtime.

Compilation pattern:

LOAD_CONST 1
LOAD_FAST x
LOAD_CONST 3
BUILD_TUPLE 3

The compiler distinguishes immutable constant containers from runtime-built containers.

24.8 Frozen Set Constants

Set literals are mutable, so a set itself cannot be a constant in bytecode.

But CPython may use frozenset constants for optimized membership tests.

Example:

def is_small(x):
    return x in {1, 2, 3}

The source uses a set literal. The compiler may compile membership against a frozenset constant because the set is used only for membership testing.

Conceptually:

LOAD_FAST x
LOAD_CONST frozenset({1, 2, 3})
CONTAINS_OP

This avoids rebuilding the set each time the function runs.

The compiler must preserve semantics. This optimization is valid for constants because membership in {1, 2, 3} and frozenset({1, 2, 3}) gives the same result for the relevant operation.

24.9 Nested Code Objects as Constants

Nested functions store their code objects in co_consts.

Example:

def outer():
    def inner():
        return 1
    return inner

Inspect:

for const in outer.__code__.co_consts:
    print(repr(const))

You will find a code object for inner.

At runtime, the outer function executes bytecode that loads the nested code object and creates a function object:

LOAD_CONST <code object inner>
MAKE_FUNCTION
STORE_FAST inner

The nested code object is constant compiled data. The function object is created at runtime.

24.10 Name Tables

co_names stores symbolic names used by bytecode operations that are not fast locals or closure variables.

These include:

global names
builtin lookup names
attribute names
imported module names
method names
some class body names

Example:

def f(xs):
    return len(xs)

Inspect:

print(f.__code__.co_names)

Typical shape:

('len',)

The bytecode loads len through a name index.

24.11 Globals and Builtins

A name in co_names may refer to a global or builtin at runtime.

Example:

def f(xs):
    return len(xs)

len is not local. It is loaded using global lookup rules.

Runtime lookup checks:

function globals
then builtins

The code object does not store the actual builtin function len. It stores the name string "len".

That means changing globals can affect execution:

def f(xs):
    return len(xs)

len = lambda x: 999

print(f([1, 2, 3]))

Inside that module, len now resolves to the global lambda, not the builtin.

24.12 Attribute Names

Attribute names also live in co_names.

Example:

def f(obj):
    return obj.value

Inspect:

print(f.__code__.co_names)

Typical shape:

('value',)

The bytecode uses:

LOAD_FAST obj
LOAD_ATTR value

The string "value" is stored once in co_names.

This is separate from global lookup. The same table stores name strings used by multiple instruction families.

24.13 Method Names

Method call syntax also uses names.

Example:

def f(obj):
    return obj.run()

run appears in co_names.

The compiler may emit specialized call-oriented instructions depending on Python version, but the method name still comes from the name table.

Conceptual bytecode:

LOAD_FAST obj
LOAD_METHOD run
CALL 0

The name table stores the method name string.

24.14 Import Names

Imports use name tables too.

Example:

def f():
    import os
    return os.getcwd()

The name table may contain:

os
getcwd

The local table contains os if the import is inside a function because import binds a local variable.

This distinction matters:

co_names:
    names needed by import and attribute operations

co_varnames:
    local binding created by import os

Imports are assignments from the perspective of local variable layout.

24.15 Local Variables

co_varnames stores fast local variable names.

Example:

def f(a, b):
    c = a + b
    return c

Inspect:

print(f.__code__.co_varnames)

Typical shape:

('a', 'b', 'c')

The frame stores local values in an array-like layout. Bytecode indexes into this layout.

LOAD_FAST 0    a
LOAD_FAST 1    b
STORE_FAST 2   c

This is why local access is faster than global dictionary lookup.

24.16 Parameter Locals

Function parameters appear first in co_varnames.

Example:

def f(a, b, c=0):
    d = a + b + c
    return d

co_varnames usually begins:

('a', 'b', 'c', 'd')

The call machinery binds arguments into those fast local slots before the function body begins executing.

Defaults are stored on the function object, not directly in the code object.

print(f.__defaults__)
print(f.__code__.co_varnames)

24.17 Positional-Only and Keyword-Only Locals

Code objects store argument counts separately from co_varnames.

Example:

def f(a, b, /, c, *, d):
    return a, b, c, d

Inspect:

code = f.__code__

print(code.co_posonlyargcount)
print(code.co_argcount)
print(code.co_kwonlyargcount)
print(code.co_varnames)

The local names are stored in co_varnames, while the counts define how to interpret the leading entries.

Conceptually:

co_varnames:
    a, b, c, d

co_posonlyargcount:
    2

co_argcount:
    3

co_kwonlyargcount:
    1

The call machinery uses this metadata for argument binding.

24.18 *args and **kwargs

Variable argument names also appear in co_varnames.

Example:

def f(a, *args, **kwargs):
    return args, kwargs

Inspect:

code = f.__code__

print(code.co_varnames)
print(code.co_flags)

The co_flags field records that the function accepts variable positional or keyword arguments.

The names args and kwargs are local slots.

24.19 Temporary Values Are Not Named Locals

The evaluation stack holds temporary values.

Example:

def f(a, b, c):
    return a + b * c

co_varnames contains:

a
b
c

It does not contain temporary intermediate values such as b * c.

That intermediate result lives on the frame’s evaluation stack.

Conceptual stack:

LOAD_FAST a       stack: a
LOAD_FAST b       stack: a, b
LOAD_FAST c       stack: a, b, c
BINARY_OP *       stack: a, temp
BINARY_OP +       stack: result
RETURN_VALUE      stack empty

Local variables and stack temporaries are separate storage mechanisms.

24.20 Cell Variables

co_cellvars stores locals captured by nested functions.

Example:

def outer():
    x = 1

    def inner():
        return x

    return inner

Inspect:

print(outer.__code__.co_cellvars)

Typical result:

('x',)

Inside outer, x must live in a cell because inner may access it after outer returns.

The local variable becomes heap-backed closure storage.

24.21 Free Variables

co_freevars stores names captured from enclosing scopes.

Using the same example:

inner = outer()
print(inner.__code__.co_freevars)

Typical result:

('x',)

Inside inner, x is not a normal local. It is loaded from a closure cell.

Conceptual bytecode:

LOAD_DEREF x
RETURN_VALUE

The code object records the name. The function object carries the actual cell.

24.22 The Locals Dictionary

Inside a normal optimized function, locals