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 configurando servidor HTTPS e certificado TLS com Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    HTTPSServer: crie servidor HTTPS local no Python

    Aprenda HTTPSServer no Python para servir HTTPS localmente, configurar certificados, usar threads e entender limites de segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    08/10/2026
    Arquivos protegidos representando extração segura de TAR com Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    tarfile extraction_filter: extraia TAR com segurança

    Aprenda tarfile extraction_filter no Python para extrair arquivos TAR com validação, segurança e controle de caminhos.

    Ler mais

    Tempo de leitura: 6 minutos
    08/10/2026
    Código Python exibindo avisos controlados com catch_warnings
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    catch_warnings: capture warnings em testes Python

    Aprenda catch_warnings no Python para capturar, testar e controlar avisos com filtros específicos e segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código Python representando referências persistentes do pickle
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    pickle persistent_id: serialize referências externas

    Aprenda pickle persistent_id no Python para serializar referências externas com IDs estáveis, validação, segurança e compatibilidade.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código binário representando buffers e memória no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    memoryview.count: conte valores sem copiar buffers

    Aprenda memoryview.count no Python para contar bytes e valores em buffers sem cópias, com formatos, limites e boas práticas.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026
    Código Python com anotações de tipos
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    annotationlib: evite imports circulares em anotações

    Aprenda annotationlib no Python para ler anotações adiadas, evitar imports circulares e criar ferramentas de introspecção seguras.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026