opcode no Python: explore o bytecode

Publicado em: 15/08/2026
Tempo de leitura: 6 minutos
Monitor com código binário representando instruções opcode do bytecode do Python

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:

  • hasconst acessa co_consts.
  • hasname usa nomes de co_names.
  • haslocal trabalha com variáveis locais.
  • hasfree acessa 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 Instruction a bytes crus.
  • Use baseopname em 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.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Desenvolvedor analisando consumo de memória com tracemalloc no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    tracemalloc no Python: encontre vazamentos

    Aprenda tracemalloc no Python para comparar snapshots, encontrar crescimento de memória e diagnosticar vazamentos com segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    15/08/2026
    Código com anotações de tipos representando introspecção com annotationlib no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    annotationlib no Python: leia anotações

    Aprenda annotationlib no Python 3.14 para ler anotações como valores, ForwardRef ou strings e evitar riscos de execução.

    Ler mais

    Tempo de leitura: 7 minutos
    14/08/2026
    Arquivadores organizados representando módulos importados diretamente de arquivos ZIP com zipimport no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    zipimport no Python: importe de ZIPs

    Aprenda zipimport no Python para importar módulos e pacotes de arquivos ZIP, trabalhar com loaders e evitar riscos de segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    14/08/2026
    Diagrama de diretórios representando caminhos site-packages e configuração do módulo site no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    site no Python: entenda os caminhos

    Aprenda o módulo site no Python para entender site-packages, user site, arquivos .pth, sitecustomize, usercustomize e opções de inicialização.

    Ler mais

    Tempo de leitura: 8 minutos
    14/08/2026
    Ícone de instalador representando o bootstrap offline do pip com ensurepip no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    ensurepip no Python: reinstale o pip

    Aprenda ensurepip no Python para instalar ou restaurar o pip offline, escolher ambiente, scripts, upgrade e evitar conflitos com o

    Ler mais

    Tempo de leitura: 7 minutos
    14/08/2026
    Caixa de software representando metadados de pacotes consultados com importlib.metadata no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    importlib.metadata no Python: pacotes

    Aprenda importlib.metadata no Python para consultar versões, dependências, arquivos, metadados e entry points de pacotes instalados.

    Ler mais

    Tempo de leitura: 7 minutos
    14/08/2026