Python opcode covers the numeric codes, readable names, and classifications of CPython bytecode instructions. In current Python documentation, the public opcode collections are documented with dis because the two layers are inseparable: dis decodes compiled code, while opcode tables describe and group the operations.
This subject is useful for analysis tools, debuggers, interpreter research, and bytecode experiments. Bytecode remains a CPython implementation detail. Instructions can change between releases, are not portable to other Python virtual machines, and should never become a permanent application contract.
Opcodes and bytecode
When CPython compiles a function, it produces a code object containing instructions. Each instruction has a numeric opcode, a readable name, and often an argument.
import dis
def add(a, b):
return a + b
for instruction in dis.get_instructions(add):
print(
instruction.opcode,
instruction.opname,
instruction.arg,
instruction.argrepr,
)Prefer dis.get_instructions() over parsing co_code manually. It accounts for extended arguments, source positions, inline caches, and version-specific layouts.
Public opcode collections
Current documented collections include opname, opmap, hasarg, hasconst, hasfree, hasname, haslocal, and hasjump, exposed through dis.
import dis
print(dis.opmap['LOAD_CONST'])
print(dis.opname[dis.opmap['RETURN_VALUE']])
print(dis.hasconst[:5])The internal opcode module contributes to the implementation, but libraries should rely on documented dis interfaces whenever possible.
Map a name to its numeric code
dis.opmap maps operation names to integers.
code = dis.opmap.get('LOAD_FAST')
if code is None:
raise RuntimeError('Opcode is unavailable in this version')
print(code)Use get() when supporting multiple Python versions. Operations are regularly added, renamed, merged, or removed. Python 3.14, for example, includes new instructions for borrowed references, templates, monitoring, and common constants.
Map a number to a name
dis.opname is indexed by numeric opcode.
for number, name in enumerate(dis.opname):
if not name.startswith('<'):
print(number, name)Reserved entries can have placeholder names. Not every value is necessarily a real instruction in final executable bytecode.
Detect operations with arguments
dis.hasarg lists operations that use an argument. It is more appropriate than the historical HAVE_ARGUMENT threshold for modern tools.
for instruction in dis.get_instructions(add):
if instruction.opcode in dis.hasarg:
print(instruction.opname, instruction.arg)The meaning of an argument depends on the operation. It may index constants, names, locals, closure cells, comparison types, flags, or jump deltas.
Constants, names, locals, and closures
Specialized collections classify argument usage:
hasconstaccesses entries inco_consts.hasnamerefers to names inco_names.haslocaluses local-variable slots.hasfreeworks with cells and closure variables.
def classify(instruction):
op = instruction.opcode
if op in dis.hasconst:
return 'constant'
if op in dis.hasname:
return 'name'
if op in dis.haslocal:
return 'local'
if op in dis.hasfree:
return 'closure'
return 'other'For compiler scope classification before bytecode generation, read the Python symtable guide.
Jumps and control flow
dis.hasjump collects operations with a jump target. Modern CPython uses relative jumps, and target representation has changed across releases.
for instruction in dis.get_instructions(function):
if instruction.opcode in dis.hasjump:
print(instruction.opname, instruction.jump_target)Use Instruction.jump_target instead of calculating targets from bytes. Inline caches, logical labels, and backward-jump rules make raw arithmetic fragile.
Compute stack effect
dis.stack_effect() reports how an instruction changes the evaluation stack.
for instruction in dis.get_instructions(add):
effect = dis.stack_effect(
instruction.opcode,
instruction.arg,
)
print(instruction.opname, effect)For conditional operations, pass jump=True or jump=False to compare paths. The default returns the maximum possible effect.
Specialized instructions
Since Python 3.11, the interpreter can adapt and specialize bytecode at runtime. An Instruction object exposes baseopcode and baseopname for the general operation behind a specialized form.
for instruction in dis.get_instructions(
function,
adaptive=True,
):
print(instruction.opname, instruction.baseopname)Tools that search only exact specialized names may fail after a function warms up. Grouping by baseopname is often more meaningful.
Inline caches
Some operations reserve cache entries for specialization. Current Instruction.cache_info provides structured information.
for instruction in dis.get_instructions(function):
if instruction.cache_info:
print(instruction.opname, instruction.cache_info)Do not decode populated cache bytes as ordinary opcodes. Their data can resemble arbitrary instructions.
Pseudo-instructions
The compiler uses pseudo-instructions that are replaced or removed before final bytecode is assembled. Modern collections may also include instrumented operations.
When examining executable code, iterate over a real code object with get_instructions() rather than assuming every entry in a global table can appear at runtime.
Comparison operations
dis.cmp_op lists readable comparison names associated with COMPARE_OP. The raw argument can also contain flags, so version-aware tools should use argrepr.
code = compile('a <= b', '<expr>', 'eval')
for instruction in dis.get_instructions(code):
if instruction.opname == 'COMPARE_OP':
print(instruction.argrepr)Count instruction types
from collections import Counter
import dis
def count_operations(obj):
return Counter(
instruction.baseopname
for instruction in dis.get_instructions(obj)
)
print(count_operations(add))This report is useful for education and structural comparison, but instruction counts are not reliable performance estimates. The runtime cost depends on objects, caches, calls, memory, and specialization.
Analyze source without running it
A string can be compiled and disassembled without executing the result.
code = compile(
'result = function(value)',
'<analysis>',
'exec',
)
for instruction in dis.get_instructions(code):
print(instruction.opname)Compilation of huge or deeply nested input can still consume resources. Apply size and time limits to user-provided source.
Do not build opcode blacklists
Blocking operations such as IMPORT_NAME, LOAD_ATTR, or CALL does not create a secure sandbox. Allowed objects can expose powerful capabilities indirectly, and compiler changes invalidate the list.
Run untrusted code in a separate process or container with limited permissions, CPU, memory, network, filesystem access, and execution time.
Relationship with dis
The Python dis guide covers full disassembly, Bytecode objects, source positions, adaptive caches, and tracebacks. Opcode analysis is the narrower layer focused on instruction tables and categories.
Do not confuse pickle opcodes
The Python pickletools guide also works with opcodes, but those instructions belong to the pickle virtual machine, not CPython bytecode. The formats and security properties are unrelated.
Version compatibility
Never persist numeric opcode values as a long-term protocol. Store the exact Python version and implementation with every report and regenerate analysis when environments change.
Tools that target several interpreters should inspect platform.python_implementation() and reject unsupported virtual machines.
Testing strategy
Test normal functions, closures, classes, comprehensions, generators, coroutines, pattern matching, exceptions, and annotation scopes. Run the suite on every supported Python version.
Exercise cold and adaptive bytecode, cache information, jump targets, and source positions. Avoid rigid snapshots of full textual disassembly; assert semantic properties instead.
Recommended practices
- Use documented
disAPIs. - Prefer
Instructionobjects to raw bytes. - Group specialized code with
baseopname. - Account for caches and pseudo-instructions.
- Record the Python version and implementation.
- Never use an opcode blacklist as a sandbox.
- Do not assume numeric stability.
- Test every target release.
Conclusion
Python opcode is the metadata layer behind CPython instructions. Through collections documented in dis, tools can map names, numeric codes, arguments, jumps, constants, closures, and stack effects.
Use this information for analysis and learning, not as a portable contract. Consult the official opcode collections documentation and the official CPython opcode source.







