A verdade é que muitos devs ainda não sabem como o Python roda seu código… você sabe? 🤫

  1. Leitura → Parsing Quando você roda python app.py, o CPython (versão "oficial" do Python, escrita em C) lê todo o texto e verifica se a sintaxe faz sentido. Pense num corretor ortográfico que procura vírgulas fora do lugar.

  2. Tradução rápida → Bytecode Em seguida, o CPython faz uma tradução intermediária chamada bytecode – é como transformar português em "portunhol" antes de chegar ao espanhol. Arquivo gerado: __pycache__/app.cpython-312.pyc

    Exemplo de bytecode:

    0 LOAD_CONST 1 (10) # carrega o número 10
    2 LOAD_CONST 2 (20) # carrega o número 20
    4 BINARY_ADD        # soma 10 + 20
    6 RETURN_VALUE      # devolve 30
    
  3. Execução → Python Virtual Machine Agora quem assume é a Python VM, um programa que interpreta o bytecode linha a linha, decidindo o que fazer em cada etapa (alocar memória, somar valores, coletar lixo, etc.).

  4. Cache esperto → __pycache__ O bytecode é guardado na pasta __pycache__. Da próxima vez que você rodar o mesmo arquivo, essa etapa de tradução é pulada e o script abre mais rápido.

Resumindo em uma frase

Python compila para bytecode e depois interpreta esse bytecode. Por isso dizemos que ele é ao mesmo tempo compilado e interpretado – combina produtividade com portabilidade.

Sobre o diretório __pycache__, vale lembrar que ele só guarda os arquivos de módulos que são importados.

Ou seja, suponha que eu rodo python meu_script.py e dentro dele tem um import meu_modulo. Então no diretório __pycache__ será gerado o arquivo correspondente ao meu_modulo.py (geralmente no formato arquivo-versão.pyc, então no meu caso o arquivo resultante seria __pycache__/meu_modulo.cpython-312.pyc já que estou usando Python 3.12). Mas não será gerado um arquivo correspondente ao meu_script.py.

Inclusive, se eu rodar o código do módulo diretamente (por exemplo, python meu_modulo.py em vez de rodar outro código que o importe), neste caso ele não é adicionado ao __pycache__.

Isso está descrito na documentação, que também mostra que vc pode adicionar manualmente qualquer arquivo ao __pycache__, usando o módulo py_compile. Ex:

import py_compile
py_compile.compile('arquivo.py')

Desta forma, vc pode adicionar qualquer arquivo ao __pycache__. Mas por padrão, ele terá apenas os módulos que são importados no código que estiver rodando.


Inclusive, na PEP 3147 (que definiu o mecanimo por trás do diretório __pycache__) tem um diagrama bem didático, e repare como o ponto inicial é um import:

Diagrama do mecanismo de importação de um módulo

Muito bom, eu como um amante do Python gosto de ver conteúdos bons sobre ele... Aqui vai um link de uma discussão legal sobre cpython