dis module

Updated April 7, 2026 · Modules
stdlib bytecode debugging disassembler performance

The dis module disassembles Python bytecode — it converts the compiled bytecode that CPython actually executes into a human-readable format. When you want to understand what your code is really doing, or why something performs the way it does, bytecode is where you look.

Bytecode is CPython’s internal representation. The dis module reads bytecode defined in the interpreter and exposes it for analysis. This is a CPython-specific tool: other Python implementations (PyPy, MicroPython) may use different bytecode entirely.

dis.dis() — Disassemble a Function or Code Object

The main entry point:

import dis

def add_one(x):
    return x + 1

dis.dis(add_one)
  1 RESUME 0
  2 LOAD_FAST 0 (x)
      LOAD_CONST 1 (1)
      BINARY_OP 0 (+)
      RETURN_VALUE

For a module or class, dis.dis() recursively disassembles all functions or methods:

dis.dis(sys)  # disassembles all functions in the sys module

If called with no argument, disassembles the last traceback:

dis.dis()

dis.Bytecode — The Bytecode Analysis Object

Bytecode wraps code for detailed inspection:

bc = dis.Bytecode(add_one)
for instr in bc:
    print(instr.opname, instr.argval)

Bytecode accepts several keyword arguments. first_line sets which line number to report as the first line in the output. current_offset marks a specific instruction as the “current” one (displayed with -->). show_caches (3.13+) displays inline cache entries used by the interpreter to specialize bytecode. adaptive shows the specialized bytecode that CPython generates at runtime. show_offsets includes instruction offsets. show_positions (3.14+) includes source positions.

from_traceback() constructs a Bytecode object from a traceback and sets current_offset to the instruction that raised the exception:

try:
    something_that_fails()
except Exception:
    dis.dis(distb())

dis.get_instructions() — Iterate Over Instructions

Returns an iterator of Instruction named tuples:

for instr in dis.get_instructions(add_one):
    print(instr.opname, instr.argrepr)

Each Instruction has these attributes:

AttributeDescription
opnameHuman-readable opcode name (e.g., LOAD_FAST)
opcodeNumeric opcode value
arg / opargNumeric argument (or None)
argvalResolved argument value (e.g., a variable name)
argreprHuman-readable argument description
offsetBytecode index of this instruction
starts_lineTrue if this starts a source line
line_numberSource line number (or None)
is_jump_targetTrue if another instruction jumps here
jump_targetTarget offset if this is a jump instruction
cache_infoCache entry information (3.13+)

dis.code_info() and dis.show_code()

code_info() returns a formatted multi-line string with details about a code object:

print(dis.code_info(add_one))
Name:              add_one
Filename:          <stdin>
Argument count:    1
Positional-only arguments: 0
Kw-only arguments: 0
Non-default kwargs: 0
First line:       1
Free variables:   ()
Cell variables:   ()
...

show_code() is a convenience wrapper that prints the same information to a file (or sys.stdout):

dis.show_code(add_one)                         # print to stdout
dis.show_code(my_function, file=f)             # print to file object f

dis.distb() — Disassemble a Traceback

Disassembles the top-of-stack function from a traceback, marking the failing instruction:

import traceback
dis.distb()
# same as: dis.distb(sys.exc_info()[2])

dis.findlinestarts() — Line Start Offsets

Yields (offset, lineno) pairs for each line start in bytecode:

list(dis.findlinestarts(add_one.__code__))
# [(0, 1), (2, 2)]

Line numbers can be None in bytecode that does not map to source lines (added in 3.13).

dis.findlabels() — Jump Target Offsets

Finds all bytecode offsets that are jump targets — places another instruction can jump to:

dis.findlabels(add_one.__code__)
# returns a list of offsets

dis.stack_effect() — Compute Stack Effects

Computes how an opcode changes the stack depth. This is useful for understanding bytecode complexity:

dis.stack_effect(dis.opmap['LOAD_FAST'])    # 1 (pushes one value)
dis.stack_effect(dis.opmap['POP_TOP'])     # -1 (removes one value)
dis.stack_effect(dis.opmap['BINARY_OP'], oparg=0)  # -1 (pops 2, pushes 1)

The jump parameter controls whether to use the jump or non-jump stack effect for conditional jumps:

# For a JUMP_IF_TRUE instruction:
dis.stack_effect(opcode, oparg, jump=True)   # stack if jumping
dis.stack_effect(opcode, oparg, jump=False)  # stack if not jumping

Command-Line Interface

dis can be run as a script:

python -m dis mymodule.py       # disassemble a file
python -m dis -O mymodule.py     # show instruction offsets
python -m dis -C mymodule.py     # show inline caches (3.13+)
python -m dis -S mymodule.py     # show specialized bytecode (3.14+)
python -m dis -P mymodule.py      # show source positions (3.14+)

Common Bytecode Operations

Knowing a few opcodes helps you read dis output:

  • LOAD_FAST / LOAD_CONST — push a local or constant onto the stack
  • STORE_FAST — pop the top of stack into a local
  • CALL — call a function
  • BINARY_OP — perform a binary operation (+, -, etc.)
  • COMPARE_OP — perform a comparison
  • JUMP_IF_TRUE / JUMP_IF_FALSE — conditional jumps
  • POP_JUMP_IF_TRUE / POP_JUMP_IF_FALSE — pop and jump
  • RETURN_VALUE — return from a function
  • RESUME — marks the start of a function (3.11+)

See Also

  • python-bytecode — understanding .pyc files and how Python compiles source to bytecode
  • debugging-with-pdb — using the Python debugger, which builds on inspection utilities
  • python-gil-explained — using bytecode analysis to reason about Python’s thread model