opcode en Python abarca los códigos numéricos, nombres legibles y clasificaciones de las instrucciones de bytecode de CPython. En la documentación actual, las colecciones públicas aparecen junto a dis: dis desmonta el código compilado y las tablas de opcode describen las operaciones.
El tema es útil para analizadores, depuradores, investigación del intérprete y experimentos educativos. El bytecode es un detalle de implementación. Las instrucciones cambian entre versiones, no son portables a otras máquinas virtuales y no deben convertirse en contrato permanente de una aplicación.
Opcodes y bytecode
Cuando CPython compila una función, produce un objeto de código con instrucciones. Cada una posee código numérico, nombre y, con frecuencia, argumento.
import dis
def sumar(a, b):
return a + b
for instruccion in dis.get_instructions(sumar):
print(
instruccion.opcode,
instruccion.opname,
instruccion.arg,
instruccion.argrepr,
)Prefiere dis.get_instructions() frente a interpretar co_code manualmente. La API considera argumentos extendidos, posiciones, caches y cambios de versión.
Colecciones públicas
Las colecciones documentadas incluyen opname, opmap, hasarg, hasconst, hasfree, hasname, haslocal y hasjump, expuestas por dis.
import dis
print(dis.opmap['LOAD_CONST'])
print(dis.opname[dis.opmap['RETURN_VALUE']])
print(dis.hasconst[:5])El módulo interno opcode participa en la implementación, pero las bibliotecas deberían usar interfaces documentadas de dis.
Mapea nombre a número
dis.opmap transforma nombres de operaciones en enteros.
codigo = dis.opmap.get('LOAD_FAST')
if codigo is None:
raise RuntimeError('Opcode no disponible')
print(codigo)Usa get() si soportas varias versiones. Las operaciones pueden añadirse, renombrarse, fusionarse o eliminarse. Python 3.14 incluye instrucciones nuevas para referencias prestadas, templates, monitorización y constantes comunes.
Mapea número a nombre
dis.opname se indexa con el opcode numérico.
for numero, nombre in enumerate(dis.opname):
if not nombre.startswith('<'):
print(numero, nombre)Las entradas reservadas pueden tener nombres de marcador. No todos los valores representan instrucciones reales del bytecode final.
Detecta operaciones con argumento
dis.hasarg enumera operaciones que utilizan argumento. Es preferible al límite histórico HAVE_ARGUMENT.
for instruccion in dis.get_instructions(sumar):
if instruccion.opcode in dis.hasarg:
print(instruccion.opname, instruccion.arg)El significado depende de la operación: índice de constante, nombre, variable local, celda de closure, flags o delta de salto.
Constantes, nombres, locales y closures
Las colecciones especializadas clasifican argumentos:
hasconstaccede aco_consts.hasnameusa nombres deco_names.haslocalusa variables locales.hasfreetrabaja con celdas y closures.
def clasificar(instruccion):
op = instruccion.opcode
if op in dis.hasconst:
return 'constante'
if op in dis.hasname:
return 'nombre'
if op in dis.haslocal:
return 'local'
if op in dis.hasfree:
return 'closure'
return 'otro'Para la clasificación de ámbitos antes del bytecode, consulta symtable en Python.
Saltos y flujo de control
dis.hasjump agrupa operaciones con objetivo de salto. CPython moderno usa saltos relativos y su representación ha cambiado.
for instruccion in dis.get_instructions(funcion):
if instruccion.opcode in dis.hasjump:
print(instruccion.opname, instruccion.jump_target)Usa Instruction.jump_target en lugar de calcular destinos con bytes. Caches, labels lógicos y saltos hacia atrás vuelven frágil la aritmética manual.
Calcula el efecto de pila
dis.stack_effect() informa cómo cambia la pila de evaluación.
for instruccion in dis.get_instructions(sumar):
efecto = dis.stack_effect(
instruccion.opcode,
instruccion.arg,
)
print(instruccion.opname, efecto)Para operaciones condicionales, pasa jump=True o jump=False. El valor predeterminado devuelve el efecto máximo.
Instrucciones especializadas
Desde Python 3.11, el intérprete adapta y especializa bytecode durante la ejecución. Instruction expone baseopcode y baseopname para identificar la operación general.
for instruccion in dis.get_instructions(
funcion,
adaptive=True,
):
print(instruccion.opname, instruccion.baseopname)Las herramientas que buscan nombres especializados exactos pueden fallar cuando la función se calienta. Agrupar por baseopname suele ser mejor.
Caches inline
Algunas operaciones reservan entradas de cache. Instruction.cache_info entrega información estructurada.
for instruccion in dis.get_instructions(funcion):
if instruccion.cache_info:
print(instruccion.opname, instruccion.cache_info)No interpretes bytes de cache como instrucciones normales; sus datos pueden parecer opcodes arbitrarios.
Pseudoinstrucciones
El compilador utiliza operaciones que se sustituyen o eliminan antes del bytecode final. Las colecciones modernas también pueden incluir instrucciones instrumentadas.
Para código ejecutable, itera un objeto real con get_instructions() y no presupongas que cada entrada global puede aparecer en runtime.
Operaciones de comparación
dis.cmp_op contiene nombres de comparaciones asociadas a COMPARE_OP. El argumento bruto puede incluir flags, por lo que conviene usar argrepr.
codigo = compile('a <= b', '<expr>', 'eval')
for instruccion in dis.get_instructions(codigo):
if instruccion.opname == 'COMPARE_OP':
print(instruccion.argrepr)Cuenta tipos de instrucciones
from collections import Counter
import dis
def contar(objeto):
return Counter(
instruccion.baseopname
for instruccion in dis.get_instructions(objeto)
)
print(contar(sumar))El reporte es útil para aprendizaje y comparación estructural, pero la cantidad de instrucciones no estima bien el rendimiento. El costo depende de objetos, llamadas, caches y memoria.
Analiza fuente sin ejecutarla
Una string puede compilarse y desmontarse sin ejecutar el objeto.
codigo = compile(
'resultado = funcion(valor)',
'<analisis>',
'exec',
)
for instruccion in dis.get_instructions(codigo):
print(instruccion.opname)Compilar entradas enormes todavía consume recursos. Aplica límites a fuente externa.
No construyas blacklists de opcode
Bloquear IMPORT_NAME, LOAD_ATTR o CALL no crea una sandbox. Los objetos permitidos pueden ofrecer rutas indirectas a capacidades peligrosas y los cambios del compilador rompen la lista.
Ejecuta código no confiable en otro proceso o contenedor con permisos, CPU, memoria, red, sistema de archivos y tiempo limitados.
Relación con dis
La guía de dis en Python explica desmontaje completo, objetos Bytecode, posiciones, caches y tracebacks. Opcode es la capa más específica de tablas y categorías.
No confundas opcodes de pickle
La guía de pickletools en Python también trabaja con opcodes, pero pertenecen a la máquina virtual de pickle, no al bytecode de CPython.
Compatibilidad
No persistas números como protocolo duradero. Guarda la versión exacta y la implementación junto a cada reporte y regenera el análisis cuando cambia el entorno.
Una herramienta multiplataforma debe comprobar platform.python_implementation() y rechazar máquinas virtuales no soportadas.
Pruebas
Prueba funciones, closures, clases, comprehensions, generadores, coroutines, pattern matching, excepciones y ámbitos de anotación. Ejecuta la suite en cada versión soportada.
Incluye bytecode frío y adaptativo, caches, saltos y posiciones. Evita snapshots textuales rígidos; verifica propiedades semánticas.
Buenas prácticas
- Usa APIs documentadas de
dis. - Prefiere objetos
Instruction. - Agrupa especializaciones con
baseopname. - Considera caches y pseudoinstrucciones.
- Registra versión e implementación.
- No uses blacklists como sandbox.
- No presupongas estabilidad numérica.
- Prueba cada versión objetivo.
Conclusión
opcode en Python es la capa de metadatos de las instrucciones de CPython. Mediante las colecciones documentadas en dis, una herramienta puede mapear nombres, números, argumentos, saltos, constantes, closures y efectos de pila.
Úsalo para análisis y aprendizaje, no como contrato portable. Consulta la documentación oficial de colecciones de opcode y el código fuente oficial de opcode en CPython.







