dis
The Python dis module is CPython’s bytecode disassembler. It translates compiled bytecode into human-readable instructions, enabling developers to inspect how Python source code is executed by the interpreter.
Here’s a quick look at disassembling a simple function:
>>> import dis
>>> def greet(name):
... msg = "Hello, " + name
... return msg
...
>>> dis.dis(greet)
1 RESUME 0
2 LOAD_CONST 0 ('Hello, ')
LOAD_FAST_BORROW 0 (name)
BINARY_OP 0 (+)
STORE_FAST 1 (msg)
3 LOAD_FAST_BORROW 1 (msg)
RETURN_VALUE
Each row shows a source line number, an instruction name (opcode), and any argument with its resolved value in parentheses. Exact instruction names vary across CPython versions.
Key Features
- Disassembles functions, methods, classes, modules, code objects, and source strings
- Provides programmatic access to instructions via
get_instructions()and theBytecodeclass - Returns source line numbers, jump targets, and argument details for each instruction
- Exposes code object metadata, such as variable names, constants, and argument counts
- Includes a command-line interface,
python -m dis - Supports adaptive (specialized) bytecode inspection
Frequently Used Classes and Functions
| Object | Type | Description |
|---|---|---|
dis.dis() |
Function | Disassembles a function, class, module, or source string and prints the result |
dis.get_instructions() |
Function | Returns an iterator of Instruction objects for each bytecode operation |
dis.code_info() |
Function | Returns a formatted string with code object metadata such as variable names and constants |
dis.show_code() |
Function | Prints code object metadata to stdout |
dis.findlinestarts() |
Function | Yields (offset, lineno) pairs for each source line start in a code object |
dis.findlabels() |
Function | Returns a list of bytecode offsets that are jump targets |
dis.Bytecode |
Class | Iterable wrapper around a code object that yields Instruction instances |
dis.Instruction |
Class | Named tuple holding the opcode, argument, source line, and position for one instruction |
dis.Positions |
Class | Holds fine-grained source location info (line, column) attached to each instruction |
Examples
Using get_instructions() to iterate over instructions programmatically:
>>> import dis
>>> def double(x):
... return x * 2
...
>>> [instr.opname for instr in dis.get_instructions(double)]
[
'RESUME',
'LOAD_FAST_BORROW',
'LOAD_SMALL_INT',
'BINARY_OP',
'RETURN_VALUE'
]
Using the Bytecode class to capture disassembly as a string and inspect code metadata:
>>> import dis
>>> def double(x):
... return x * 2
...
>>> bc = dis.Bytecode(double)
>>> print(bc.dis())
1 RESUME 0
2 LOAD_FAST_BORROW 0 (x)
LOAD_SMALL_INT 2
BINARY_OP 5 (*)
RETURN_VALUE
>>> print(bc.info())
Name: double
Filename: <stdin>
Argument count: 1
...
Inspecting code object details with the code_info() function:
>>> import dis
>>> def greet(name):
... return "Hello, " + name
...
>>> print(dis.code_info(greet))
Name: greet
Filename: <stdin>
Argument count: 1
Positional-only arguments: 0
Kw-only arguments: 0
Number of locals: 1
Stack size: 2
Flags: OPTIMIZED, NEWLOCALS
Constants:
0: 'Hello, '
Variable names:
0: name
Common Use Cases
The most common tasks for dis include:
- Comparing two implementations of the same logic to understand which generates fewer instructions
- Tracing the source of unexpected behavior by examining the exact instructions a code path executes
- Understanding how Python features, such as comprehensions, closures, and generators compile to bytecode
- Building static analysis tools that inspect function bytecode programmatically
- Exploring CPython internals and learning how the Python virtual machine works
Real-World Example
The following script uses dis to compare two string-joining implementations and count their instructions:
compare_str_join.py
import dis
def join_manual(words):
result = ""
for word in words:
result = result + word + ", "
return result.rstrip(", ")
def join_builtin(words):
return ", ".join(words)
for func in (join_manual, join_builtin):
count = sum(1 for _ in dis.get_instructions(func))
print(f"{func.__name__}: {count} instructions")
print()
print("join_builtin disassembly:")
dis.dis(join_builtin)
Run it:
$ python compare_str_join.py
join_manual: 20 instructions
join_builtin: 6 instructions
join_builtin disassembly:
1 RESUME 0
2 LOAD_CONST 0 (', ')
LOAD_ATTR 1 (join + NULL|self)
LOAD_FAST_BORROW 0 (words)
CALL 1
RETURN_VALUE
The instruction count confirms that join_builtin() compiles to significantly fewer bytecode operations, reflecting the efficiency advantage of the built-in str.join() method over manual concatenation in a loop.
Related Resources
Tutorial
Your Guide to the CPython Source Code
In this detailed Python tutorial, you'll explore the CPython source code. By following this step-by-step walkthrough, you'll take a deep dive into how the CPython compiler works and how your Python code gets executed.
For additional information on related topics, take a look at the following resources:
By Leodanis Pozo Ramos • Updated April 2, 2026