O módulo opcode no Python reúne nomes, códigos numéricos e classificações das instruções usadas pelo bytecode do CPython. Nas versões atuais, a documentação pública dessas coleções aparece junto ao módulo dis, porque os dois recursos são inseparáveis: dis desmonta o código, enquanto as tabelas de opcode descrevem e agrupam as operações.
Esse tema é útil para ferramentas de análise, depuradores, estudos sobre o interpretador e validações de bytecode. Entretanto, bytecode é detalhe de implementação. Instruções podem mudar entre versões, não são portáveis para outras máquinas virtuais Python e não devem sustentar regras essenciais de negócio.
Opcode e bytecode
Ao compilar uma função, o CPython produz um objeto de código com uma sequência de instruções. Cada instrução possui um código numérico, um nome legível e, em muitos casos, um argumento.
import dis
def somar(a, b):
return a + b
for instrucao in dis.get_instructions(somar):
print(
instrucao.opcode,
instrucao.opname,
instrucao.arg,
instrucao.argrepr,
)Usar dis.get_instructions() é preferível a interpretar co_code manualmente, pois ele considera argumentos estendidos, posições, caches e mudanças da versão.
Onde estão as coleções
Em Python atual, coleções documentadas como opname, opmap, hasarg, hasconst, hasfree, hasname e hasjump são expostas pelo módulo dis.
import dis
print(dis.opmap['LOAD_CONST'])
print(dis.opname[dis.opmap['RETURN_VALUE']])
print(dis.hasconst[:5])O módulo interno opcode também participa da implementação, mas bibliotecas devem preferir as interfaces documentadas de dis.
Mapeie nome para número
dis.opmap é um dicionário que transforma o nome de uma operação no código numérico correspondente.
codigo = dis.opmap.get('LOAD_FAST')
if codigo is None:
raise RuntimeError('Opcode não existe nesta versão')
print(codigo)Use get() ao lidar com várias versões, pois uma instrução pode ser adicionada, renomeada ou removida. No Python 3.14, por exemplo, existem novas operações associadas a referências emprestadas, templates e monitoramento.
Mapeie número para nome
dis.opname é uma sequência indexada pelo código numérico.
for numero in range(len(dis.opname)):
nome = dis.opname[numero]
if not nome.startswith('<'):
print(numero, nome)Entradas reservadas ou não utilizadas podem aparecer com nomes especiais. Não presuma que todos os valores de 0 a 255 correspondem a instruções válidas para bytecode final.
Identifique instruções com argumentos
dis.hasarg lista operações que utilizam argumento. Essa coleção deve ser preferida ao antigo limite HAVE_ARGUMENT, que está obsoleto para análises modernas.
instrucao = next(dis.get_instructions(somar))
if instrucao.opcode in dis.hasarg:
print(instrucao.arg)Mesmo quando uma operação recebe um número, a interpretação depende da operação: índice de constante, nome, variável local, deslocamento de salto ou flags compactadas.
Constantes, nomes e variáveis livres
As coleções especializadas ajudam a classificar argumentos:
hasconstacessaco_consts.hasnameusa nomes deco_names.haslocaltrabalha com variáveis locais.hasfreeacessa células e variáveis de closure.
def classificar(instrucao):
op = instrucao.opcode
if op in dis.hasconst:
return 'constante'
if op in dis.hasname:
return 'nome'
if op in dis.haslocal:
return 'local'
if op in dis.hasfree:
return 'closure'
return 'outro'Para entender como nomes são classificados pelo compilador antes do bytecode, consulte symtable no Python.
Saltos e fluxo de controle
dis.hasjump reúne opcodes com alvo de salto. Em versões recentes, saltos são relativos e a representação mudou várias vezes.
for instrucao in dis.get_instructions(somar):
if instrucao.opcode in dis.hasjump:
print(instrucao.opname, instrucao.jump_target)Prefira Instruction.jump_target a cálculos manuais. Caches inline, labels lógicos e diferenças entre saltos para frente e para trás tornam a aritmética bruta frágil.
Efeito sobre a pilha
dis.stack_effect() calcula quantos itens uma instrução adiciona ou remove da pilha de avaliação.
for instrucao in dis.get_instructions(somar):
efeito = dis.stack_effect(
instrucao.opcode,
instrucao.arg,
)
print(instrucao.opname, efeito)Para operações condicionais, use jump=True ou jump=False para comparar os caminhos. O valor padrão retorna o efeito máximo.
Instruções especializadas
Desde o Python 3.11, o interpretador pode adaptar bytecode durante a execução. Uma instrução especializada possui baseopcode e baseopname, que indicam a operação geral.
for instrucao in dis.get_instructions(funcao, adaptive=True):
print(
instrucao.opname,
instrucao.baseopname,
)Ferramentas que procuram padrões sem considerar a operação-base podem deixar de reconhecer bytecode aquecido.
Caches inline
Algumas instruções reservam entradas CACHE para dados de especialização. Instruction.cache_info fornece informação estruturada nas versões atuais.
for instrucao in dis.get_instructions(funcao):
if instrucao.cache_info:
print(instrucao.opname, instrucao.cache_info)Não interprete bytes de cache como opcodes comuns. Depois de preenchidos, eles podem parecer instruções arbitrárias.
Pseudo-instruções
Alguns códigos são usados pelo compilador, mas não aparecem no bytecode final. As coleções modernas podem incluir pseudo-instruções e instruções instrumentadas.
Quando uma ferramenta analisa código realmente executável, trabalhe com dis.get_instructions() sobre um objeto de código, em vez de iterar toda a tabela e presumir que cada entrada pode aparecer.
Compare operações
dis.cmp_op lista nomes de comparações usados por COMPARE_OP. O argumento pode conter flags além do índice, portanto não use o valor bruto sem considerar a versão.
for instrucao in dis.get_instructions(
compile('a <= b', '<expr>', 'eval')
):
if instrucao.opname == 'COMPARE_OP':
print(instrucao.argrepr)argrepr já oferece uma descrição legível e é mais estável para relatórios.
Crie um contador de instruções
from collections import Counter
import dis
def contar_instrucoes(objeto):
return Counter(
instrucao.baseopname
for instrucao in dis.get_instructions(objeto)
)
print(contar_instrucoes(somar))Esse tipo de relatório ajuda estudos, mas não deve ser usado sozinho para estimar desempenho. Uma instrução pode ter custo muito diferente dependendo dos objetos e caches.
Analise sem executar
Strings podem ser compiladas e desmontadas sem executar o código.
codigo = compile(
'resultado = funcao(valor)',
'<analise>',
'exec',
)
for instrucao in dis.get_instructions(codigo):
print(instrucao.opname)Compilar ainda pode consumir recursos com entradas enormes. Limite tamanho e complexidade quando a fonte vem de usuário.
Não valide segurança por blacklist de opcodes
Bloquear IMPORT_NAME, LOAD_ATTR ou CALL não cria uma sandbox confiável. Objetos permitidos podem alcançar capacidades perigosas por caminhos indiretos, e mudanças do compilador quebram a lista.
Execute código externo em processo isolado, com permissões, tempo, memória, rede e sistema de arquivos controlados.
Integração com dis
O artigo dis no Python mostra a desmontagem completa, objetos Bytecode, posições e tracebacks. O estudo de opcodes é uma camada mais específica, voltada às tabelas e classificações de instruções.
Integração com pickletools
O guia de pickletools no Python também trabalha com opcodes, mas são instruções do protocolo pickle, não bytecode do CPython. As duas máquinas possuem propósitos e tabelas diferentes.
Compatibilidade entre versões
Não persista números de opcode como contrato duradouro. Armazene a versão exata do Python junto de qualquer relatório e regenere a análise quando o ambiente mudar.
Uma ferramenta multiplataforma deve identificar a implementação com platform.python_implementation() e recusar análise incompatível.
Testes recomendados
Teste funções simples, closures, classes, comprehensions, generators, coroutines, pattern matching, tratamento de exceções e anotações. Compare resultados em cada versão suportada.
Teste bytecode frio e adaptativo, caches e posições. Não faça snapshots textuais rígidos de toda a desmontagem; prefira propriedades semânticas.
Boas práticas
- Use interfaces documentadas de
dis. - Prefira
Instructiona bytes crus. - Use
baseopnameem bytecode especializado. - Considere caches e pseudo-instruções.
- Registre versão e implementação do Python.
- Não use opcodes como sandbox.
- Não presuma estabilidade numérica.
- Teste em todas as versões-alvo.
Conclusão
O opcode no Python representa a camada de metadados das instruções do CPython. Por meio das coleções documentadas em dis, é possível mapear nomes, números, argumentos, saltos, constantes, closures e efeitos de pilha.
Use essas informações para análise e aprendizado, nunca como contrato portátil. Consulte a documentação oficial das coleções de opcode e o código-fonte oficial do opcode no CPython.







