Code Objects Internals — Core Concepts

What Is a Code Object?

Every time Python compiles a function, a class body, a module, or even a generator expression, it produces a code object. This object is an immutable container that stores everything the interpreter needs to execute that piece of code. It does not store runtime state like variable values — that job belongs to frame objects. The code object is a static description of what to do.

You can access a function’s code object through the __code__ attribute:

def add(a, b):
    return a + b

co = add.__code__

Key Attributes

Code objects expose their internals through attributes prefixed with co_:

co_code — The raw bytecode, a bytes object containing the low-level instructions the interpreter steps through. Each instruction is one or more bytes.

co_consts — A tuple of constant values used in the function. For add, this would be (None,) since there are no literal constants beyond the implicit None return.

co_varnames — A tuple of local variable names, in the order they appear. For add, this is ('a', 'b').

co_names — Names used in the function that are not local — global variables, attribute names, imported names.

co_argcount — The number of positional arguments the function accepts. For add, it is 2.

co_stacksize — The maximum depth of the evaluation stack needed to execute the bytecode. The interpreter uses this to pre-allocate the stack.

co_filename and co_firstlineno — Where the code was defined. Tracebacks use these to point you to the right file and line.

How Code Objects Relate to Functions

A function object and a code object are separate things. The function object wraps the code object and adds runtime context: default argument values, closure variables, the global namespace it was defined in. You can even attach a different code object to an existing function (though this is rarely advisable):

def multiply(a, b):
    return a * b

# Both functions share different code objects
print(add.__code__ is multiply.__code__)  # False

Code Objects Are Immutable

Once created, a code object cannot be modified. Its bytecode, constants, and variable names are all frozen. This immutability is intentional — it means the interpreter can safely share and cache code objects. When you import a module, Python stores the compiled code objects in .pyc files so it can skip recompilation next time.

If you need a modified version, you create a new code object. The code.replace() method (Python 3.8+) lets you produce a copy with specific attributes changed.

Nested Code Objects

Code objects can contain other code objects. If your function defines an inner function or a class, the inner code object appears in the outer one’s co_consts tuple:

def outer():
    def inner():
        pass
    return inner

# outer's co_consts includes inner's code object
for const in outer.__code__.co_consts:
    if hasattr(const, 'co_code'):
        print(f"Nested code: {const.co_name}")  # "inner"

This nesting mirrors the structure of your source — every scope boundary creates a new code object.

A Common Misconception

Code objects are not functions. A function is callable; a code object is not (directly). You need exec() or eval() to run a raw code object, or you wrap it in a function via types.FunctionType. Confusing the two leads to puzzlement when you try to “call” a code object and get a TypeError.

One thing to remember: The code object is the compiled, frozen blueprint of your function. It contains the bytecode instructions, the constants, and the variable layout — everything the interpreter reads at runtime. The function object is just the living wrapper that connects this blueprint to a runtime environment.

pythoncompiler-internalslanguage-implementation

See Also

  • Python Abstract Syntax Trees How Python reads your code like a recipe before cooking it — the hidden tree structure behind every program.
  • Python Bytecode Manipulation How Python secretly translates your code into tiny instructions — and how you can peek at and change those instructions yourself.
  • Python Compiler Pipeline The journey your Python code takes from text file to running program — explained like an assembly line.
  • Python Frame Objects Why Python keeps a notepad for every running function — and how it remembers where it left off.
  • Python Peephole Optimizer How Python quietly tidies up your code behind the scenes — making it faster without you lifting a finger.