dis no Python: entenda o bytecode

Publicado em: 03/08/2026
Tempo de leitura: 7 minutos
Monitor com código binário representando análise de bytecode com dis no Python

O código Python é compilado para instruções intermediárias executadas pela máquina virtual do interpretador. Essas instruções formam o bytecode do CPython. O módulo dis no Python desmonta funções, métodos, classes, coroutines e trechos de código, permitindo observar cargas de variáveis, chamadas, saltos, operadores, caches adaptativos e a relação entre linhas-fonte e operações internas.

Este guia explica dis.dis(), Bytecode, get_instructions(), code_info(), análise de tracebacks e bytecode especializado. Ele complementa nossos artigos sobre scripts Python lentos, cProfile, timeit, pdb e inspect.

O que é bytecode

Ao carregar uma função, o CPython compila sua sintaxe para um objeto de código. Esse objeto contém constantes, nomes, variáveis locais, flags, posições de origem e uma sequência de instruções.

def dobro(numero):
    return numero * 2

print(dobro.__code__)
print(dobro.__code__.co_consts)
print(dobro.__code__.co_varnames)

O bytecode não é código de máquina do processador. Ele é uma representação específica do interpretador, consumida pelo loop de avaliação do CPython.

Primeiro exemplo com dis()

dis.dis() imprime a desmontagem de uma função.

import dis

def dobro(numero):
    return numero * 2

dis.dis(dobro)

A saída pode conter operações como RESUME, LOAD_FAST, LOAD_CONST, BINARY_OP e RETURN_VALUE. Os nomes e detalhes variam entre versões.

Bytecode é detalhe de implementação

A documentação oficial de dis enfatiza que o bytecode é um detalhe do CPython. Instruções podem ser adicionadas, removidas ou alteradas entre versões e outros interpretadores podem utilizar modelos diferentes.

Use dis para aprendizado, diagnóstico, testes de compilador e ferramentas vinculadas a uma versão. Não use opcodes como contrato portátil de negócio.

Analisar strings e objetos de código

dis() também aceita uma string de código, um módulo, uma classe, um método, uma coroutine ou um objeto retornado por compile().

dis.dis("resultado = soma(a, b)")

codigo = compile(
    "total = preco * quantidade",
    "exemplo.py",
    "exec",
)
dis.dis(codigo)

Ao receber uma classe, a função percorre métodos e objetos de código aninhados até a profundidade configurada.

Controlar a profundidade

Funções aninhadas, comprehensions e generator expressions possuem objetos de código próprios. O argumento depth controla a recursão.

def externa():
    def interna(valor):
        return valor + 1
    return [interna(x) for x in range(3)]

dis.dis(externa, depth=0)
dis.dis(externa, depth=2)

Use uma profundidade pequena ao analisar módulos ou classes grandes para manter a saída legível.

Informações do objeto de código

code_info() retorna um resumo com quantidade de argumentos, variáveis, constantes, nomes, flags e tamanho da pilha.

print(dis.code_info(externa))

show_code() imprime o mesmo tipo de informação diretamente.

dis.show_code(externa)

Esses dados são úteis antes de examinar as instruções individuais.

A classe Bytecode

dis.Bytecode reúne a função analisada e permite obter informações formatadas ou iterar por instruções.

bytecode = dis.Bytecode(dobro)

print(bytecode.info())
print(bytecode.dis())

for instrucao in bytecode:
    print(instrucao.opname, instrucao.argrepr)

A classe aceita funções, generators, coroutines, strings e objetos de código.

get_instructions() para dados estruturados

Quando uma ferramenta precisa processar operações em vez de imprimir texto, use get_instructions().

for instrucao in dis.get_instructions(dobro):
    print({
        "operacao": instrucao.opname,
        "offset": instrucao.offset,
        "argumento": instrucao.argval,
        "linha": instrucao.line_number,
    })

Cada item é um objeto Instruction com opcode numérico, nome, argumento resolvido, posição, destino de salto e informações de cache.

Entender LOAD e STORE

Operações que começam com LOAD colocam referências ou valores na pilha de avaliação. STORE retira um valor e o associa a uma variável, atributo ou coleção.

def exemplo(valor):
    resultado = valor + 10
    return resultado

dis.dis(exemplo)

LOAD_FAST acessa variáveis locais. LOAD_CONST carrega constantes. STORE_FAST grava em um slot local. Globais, atributos e closures possuem instruções próprias.

A pilha de avaliação

Muitas instruções consomem operandos do topo da pilha e colocam um resultado. Em uma soma, por exemplo, o interpretador carrega os dois valores e executa uma operação binária.

def somar(a, b):
    return a + b

dis.dis(somar)

dis.stack_effect() calcula a mudança líquida causada por um opcode.

opcode = dis.opmap["LOAD_CONST"]
print(dis.stack_effect(opcode, 0))

Essa função interessa a ferramentas que validam ou simulam fluxos de bytecode.

Saltos e estruturas de controle

if, loops, short circuit e tratamento de exceções são representados por comparações e saltos.

def classificar(numero):
    if numero >= 0:
        return "positivo"
    return "negativo"

dis.dis(classificar, show_offsets=True)

Versões recentes exibem labels lógicos para destinos. Não calcule manualmente offsets sem considerar caches e mudanças específicas da versão.

Posições de código no Python 3.14

O Python 3.14 adicionou show_positions=True e a opção de linha de comando -P. A saída pode incluir linha inicial, linha final e colunas cobertas por uma instrução.

dis.dis(
    classificar,
    show_offsets=True,
    show_positions=True,
)

As posições detalhadas ajudam depuradores, cobertura, análise estática e mensagens que apontam uma expressão específica.

Executar dis pela linha de comando

O módulo pode desmontar um arquivo ou código recebido pela entrada padrão.

python -m dis programa.py

Opções atuais incluem:

  • -C para caches;
  • -O para offsets;
  • -P para posições;
  • -S para bytecode especializado.

Essas opções dependem da versão instalada.

Caches inline

Desde o Python 3.11, determinadas instruções possuem espaço para caches usados na especialização do interpretador. show_caches=True revela esses dados na desmontagem.

dis.dis(dobro, show_caches=True)

Os caches pertencem logicamente à instrução anterior. Não interprete seus bytes como opcodes independentes nem modifique bytecode adaptativo bruto.

Bytecode adaptativo e especializado

O CPython pode adaptar operações após observar tipos e padrões durante a execução. Use adaptive=True ou, na CLI do Python 3.14, -S.

for _ in range(20_000):
    dobro(10)

dis.dis(dobro, adaptive=True, show_caches=True)

A especialização é dinâmica. O resultado pode variar conforme aquecimento, build, arquitetura e estado do processo.

Não confunda desmontagem com benchmark

Uma sequência menor de instruções não garante melhor desempenho. Uma única operação pode chamar código complexo, criar objetos ou executar I/O. Confirme qualquer hipótese com timeit, cProfile e métricas reais.

def com_sum(valores):
    return sum(valores)

def com_loop(valores):
    total = 0
    for valor in valores:
        total += valor
    return total

dis explica parte da diferença estrutural, mas o benchmark mede o resultado.

Analisar uma falha com distb()

dis.distb() desmonta a função no topo de um traceback e marca a instrução que provocou a exceção.

try:
    valor = (1, 2)[5]
except IndexError as erro:
    dis.distb(erro.__traceback__)

Outra opção é construir Bytecode.from_traceback().

bytecode = dis.Bytecode.from_traceback(erro.__traceback__)
print(bytecode.dis())

Esse recurso combina bem com o artigo sobre formatação de tracebacks.

Closures e variáveis livres

Funções internas podem acessar células da função externa. A desmontagem mostra operações como MAKE_CELL, LOAD_DEREF e COPY_FREE_VARS.

def multiplicador(fator):
    def aplicar(valor):
        return valor * fator
    return aplicar

dis.dis(multiplicador)

Essas instruções ajudam a visualizar como closures preservam referências.

Generators e coroutines

Generators, yield from, await e async generators possuem operações como RETURN_GENERATOR, YIELD_VALUE, SEND e GET_AWAITABLE.

async def buscar(cliente):
    return await cliente.obter()

dis.dis(buscar)

A saída muda significativamente entre versões, pois o interpretador evolui a execução assíncrona.

Comprehensions

Uma list comprehension costuma ser compilada como um objeto de código aninhado ou otimizada de forma específica pela versão.

def pares(limite):
    return [n * 2 for n in range(limite)]

dis.dis(pares, depth=2)

Observe operações de iteração, anexação e chamadas. Não transforme automaticamente a compreensão em loop apenas pela aparência do bytecode.

Opcode collections

O módulo expõe coleções para introspecção:

print(dis.opmap["RETURN_VALUE"])
print(dis.opname[dis.opmap["RETURN_VALUE"]])
print(dis.hasconst)
print(dis.hasjump)

opmap associa nomes a códigos; opname faz o caminho inverso. As coleções hasconst, hasname, hasfree e hasjump classificam opcodes.

Testes que verificam bytecode

Bibliotecas muito próximas do interpretador podem testar a presença de uma operação, mas esses testes devem ser condicionados à versão.

import sys

operacoes = {
    item.opname
    for item in dis.get_instructions(dobro)
}

if sys.implementation.name == "cpython":
    assert "RETURN_VALUE" in operacoes

Evite snapshots textuais rígidos, pois labels, offsets, caches e formatação mudam.

Não execute código não confiável

dis.dis() sobre uma string usa compilação, não execução, mas analisar módulos pode exigir importá-los antes. Imports executam código de nível superior. Faça análise de arquivos não confiáveis em processo isolado, sem segredos e com permissões limitadas.

Erros frequentes

  • Tratar opcodes como API estável.
  • Comparar bytecode de versões diferentes sem contexto.
  • Inferir desempenho apenas pela quantidade de instruções.
  • Ignorar caches e especialização.
  • Construir ferramentas dependentes de offsets antigos.
  • Importar código não confiável para analisá-lo.
  • Alterar co_code diretamente.
  • Usar snapshots textuais frágeis em testes.

Boas práticas

  • Registre versão e implementação do Python.
  • Use get_instructions() para análise estruturada.
  • Use posições e labels em vez de cálculos frágeis.
  • Confirme hipóteses de velocidade com benchmarks.
  • Limite a profundidade em objetos grandes.
  • Analise bytecode adaptativo somente após aquecimento controlado.
  • Isole código desconhecido.
  • Trate mudanças entre releases como comportamento esperado.

Conclusão

O módulo dis no Python revela como o CPython transforma construções da linguagem em operações da máquina virtual. Ele ajuda a compreender variáveis locais, chamadas, loops, closures, generators, exceções, caches e especialização do interpretador.

Essa visão é poderosa, mas específica da implementação e da versão. Use a desmontagem como ferramenta de aprendizado e diagnóstico, não como contrato portátil nem substituto de medições. Com Bytecode, get_instructions(), posições de origem e comparação controlada, você pode investigar o funcionamento interno sem depender de suposições frágeis sobre opcodes.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Tela de erro representando diagnóstico de crashes e deadlocks com faulthandler no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    faulthandler no Python: diagnostique travamentos

    Aprenda faulthandler no Python para diagnosticar crashes, deadlocks e timeouts com pilhas de threads e código nativo.

    Ler mais

    Tempo de leitura: 8 minutos
    03/08/2026
    Notebook com código representando análise de traceback e depuração no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    traceback no Python: erros e pilha

    Aprenda traceback no Python para capturar, formatar e registrar pilhas de erro com segurança, sem vazar dados ou reter memória.

    Ler mais

    Tempo de leitura: 6 minutos
    03/08/2026
    Análise de software representando introspecção de objetos com inspect no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    inspect no Python: introspecção de objetos

    Aprenda inspect no Python para analisar funções, classes, assinaturas, código-fonte, decorators, generators e frames com segurança.

    Ler mais

    Tempo de leitura: 8 minutos
    02/08/2026
    Módulo de memória representando referências fracas e caches no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    weakref no Python: referências fracas

    Aprenda weakref no Python para criar referências fracas, caches automáticos, observadores e finalizadores sem reter objetos na memória.

    Ler mais

    Tempo de leitura: 9 minutos
    28/07/2026
    Ícone de arquivo ZIP para artigo sobre zipfile no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    zipfile no Python: arquivos ZIP seguros

    Aprenda a criar, ler, validar e extrair arquivos ZIP com zipfile no Python de forma previsível e segura.

    Ler mais

    Tempo de leitura: 5 minutos
    27/07/2026
    Grafo de dependências e fluxo de tarefas em Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    graphlib no Python: ordenação topológica

    Aprenda graphlib no Python para ordenar dependências, detectar ciclos e executar tarefas independentes em paralelo.

    Ler mais

    Tempo de leitura: 6 minutos
    27/07/2026