Skip to content
Python Basics: Inspect Bytecode Instructions with dis

Python Basics: Inspect Bytecode Instructions with dis

October 27, 2026

Bytecode inspection helps explain what the interpreter compiled, especially when a name is loaded from a function argument or the global namespace. Use the structured dis API to inspect a small function without depending on unstable byte offsets.

Prepare a scratch directory

Tested on Linux with CPython 3.12.14. You need the named runtime, Bash, a text editor, and the standard mktemp, rm, and rmdir utilities. Run Python without -O so the assertions remain enabled. Any generated fixture files live inside an automatically cleaned temporary directory.

Keep using the same terminal. Stop if setup fails, before saving the source:

start_dir="$PWD"
work_dir="$(mktemp -d)" || exit 1
cd -- "$work_dir" || exit 1

Run the example

Save this as demonstrate-dis.py:

import dis

RATE = 3

def total(quantity):
    return quantity * RATE

instructions = list(dis.get_instructions(total))
locals_loaded = [item.argval for item in instructions if item.opname == "LOAD_FAST"]
globals_loaded = [item.argval for item in instructions if item.opname == "LOAD_GLOBAL"]
assert locals_loaded == ["quantity"]
assert globals_loaded == ["RATE"]
assert total(4) == 12
print("Local loads:", locals_loaded)
print("Global loads:", globals_loaded)
print("Result before change:", total(4))

RATE = 5
assert total(4) == 20
print("Result after global change:", total(4))
print("Function arguments:", total.__code__.co_argcount)
assert total.__code__.co_argcount == 1

Run it:

python3 -B demonstrate-dis.py

Observed output:

Local loads: ['quantity']
Global loads: ['RATE']
Result before change: 12
Result after global change: 20
Function arguments: 1

Read the result

get_instructions exposes named instruction fields, so the example selects only the name loads relevant to the question. quantity is a local parameter; RATE is looked up as a global when the function executes. Changing that global changes the later result without redefining the function. The assertions connect the observed instruction categories to actual behavior.

Apply it carefully

This example targets CPython 3.12, whose bytecode is an implementation detail. Opcode names and specialization behavior can change between Python releases; do not parse disassembly as a stable interchange format or use instruction counts as a timing benchmark. dis.dis(total) is useful for a complete interactive listing, but its offsets and formatting are deliberately omitted here. Disassembly is also not a security sandbox or a substitute for reviewing source.

The official reference describes the API and its options. The checks above cover the specific fixture, not every possible input or environment.

Clean up

Remove the source and then the empty practice directory:

rm -- demonstrate-dis.py
cd -- "$start_dir" || exit 1
rmdir -- "$work_dir"
unset start_dir work_dir

If the directory is not empty, inspect the remaining files rather than deleting unknown contents.

Acknowledgements

This post was created with assistance from AI.

Last updated on