Python 3.15: lazy import, frozendict e Tachyon — o que muda no código que você já escreveu
Python 3.15: lazy import, frozendict e Tachyon — o que muda no código que você já escreveu
O Python 3.15.0 saiu em 9 de outubro de 2026, depois de um rc3 de última hora para fechar as pontas do novo sistema de import. Desde então vieram 3.15.1 (21/10) e 3.15.2, e no dia 1º de outubro o 3.10 chegou ao fim da vida — ou seja, quem ainda está no 3.10 precisa escolher um alvo, e esse alvo provavelmente é o 3.15.
A mudança que mais mexe com código existente não é performance. É sintaxe: o lazy import virou recurso da linguagem (PEP 810), com a palavra-chave lazy . Junto vieram o tipo embutido frozendict (PEP 814), o sentinel , UTF-8 como padrão de codificação, um profiler de amostragem novo na biblioteca padrão (Tachyon) e mais ajustes no JIT copy-and-patch.
Para quem mantém serviço em produção, o resumo é: os ganhos são reais e concentrados em startup ; os riscos são de diagnóstico, não de comportamento de runtime. Vamos ao que importa.
O que o lazy import faz de fato
A promessa é simples: o nome é ligado ao módulo, mas o corpo do módulo só executa no primeiro uso daquele nome.
O import continua no topo do arquivo, legível, e a ordem de dependências não muda no papel. A diferença aparece no tempo: se sua CLI tem cinco subcomandos e só um usa pandas , você pagava por pandas em qualquer execução.
Existem três formas de ligar o recurso:
explícita , com lazy na linha de import (o modo recomendado);
global , via -X lazy_imports=all , PYTHON_LAZY_IMPORTS=all ou sys.set_lazy_imports("all") , que transforma todos os imports em preguiçosos;
integrada ao ecossistema , que é o que interessa a médio prazo: projetos que adotarem o modo global conseguem ganhar startup sem tocar em arquivo.
Uma pegadinha de documentação: o PEP 810 descreve três modos ( normal , all e none ) e recomenda none para depurar. O modo none foi removido do CPython antes do 3.15.0 (gh-149321) e, no 3.15.2, -X lazy_imports=none derruba o interpretador na largada com expected all or normal . Se você seguir a receita do PEP ao pé da letra, o processo não sobe. Use normal .
O ganho real está no startup, e é mensurável
Os números que circularam nas duas primeiras semanas são de ferramentas reais. O pypistats --help caiu de cerca de 104 ms para 36 ms apenas marcando os imports pesados como lazy. Um relato de produção publicado em novembro descreve uma migração para 3.15.2 em que o --help de uma CLI de back office foi de 1.314 ms para 326 ms de mediana — perto de 4x.
Onde isso vira dinheiro e tempo:
Ferramentas de linha de comando com muitos subcomandos e dependências divergentes entre eles;
cold start de funções serverless, onde cada 100 ms conta no faturamento;
coleta de testes , que importa o mundo inteiro antes do primeiro assert ;
CLIs internas e scripts operacionais que rodam muitas vezes por dia e sempre pagaram o pedágio de importar tudo.
Onde não muda quase nada: um worker WSGI/ASGI de longa duração, que resolve todos os imports no boot e amortiza isso em milhões de requisições. Se seu caso é esse, o lazy import é cosmético — o valor está em outras partes do release.
Em times que sustentam vários pontos de entrada sobre a mesma base — CLI interna, API FastAPI, worker de fila — o startup costuma ser o gargalo invisível do deploy. Na Maquinaweb, projetos sob medida em Python, Node.js, C#/.NET e Angular normalmente nascem com esse perfil de arquitetura mapeado antes da primeira linha de código, porque decidir entre "um app monolítico" e "vários entrypoints sobre uma biblioteca comum" muda o que dá para otimizar depois.
As três armadilhas que passam batido
1. O erro de import deixa de ser fail-fast. Com import preguiçoso, um ModuleNotFoundError de dependência opcional não aparece no boot — aparece no primeiro caminho de código que usa o nome, possivelmente em produção, possivelmente num branch raro. Se o seu pipeline confia em "se subiu, os imports estão OK", ele deixou de garantir isso. Contrapeso prático: um teste que toca cada ponto de entrada (ou um lazy_imports=normal no boot) devolve a garantia.
2. Módulos importados pelo efeito colateral param de registrar. Esse é o que mais quebra em código real:
O mesmo padrão aparece em decoradores que registram rotas, plugins descobertos por import, tasks de Celery e hooks de inicialização. Regra de bolso: se o módulo é importado pelo que ele faz e não pelo que ele expõe, não torne esse import lazy. Import por efeito colateral continua sendo um cheiro de design, mas o lazy import transforma o cheiro em bug silencioso.
3. Introspecção enxerga o proxy, não o valor. Com lazy_imports=all , ler um nome via mod.__dict__ , vars(mod) ou globals() devolve o proxy não resolvido — algo como <lazy_import 'pkg.ver.V'> em vez do valor. Só o acesso por atributo ( mod.V ) materializa o objeto. Casos conhecidos incluem ferramentas que imprimem a própria versão via __dict__ . E hasattr() conta como uso: ele dispara a carga. Consequência secundária: avisos emitidos no corpo do módulo, como DeprecationWarning com stacklevel=2 , passam a apontar para a linha do primeiro uso, não para a linha do import — o que muda snapshots de stderr em testes.
frozendict, sentinel e UTF-8: as mudanças pequenas que aparecem no diff
O frozendict embutido resolve um problema antigo: usar dicionário como chave de cache ou como configuração que não deve ser mutada. Antes era MappingProxyType , immutables.Map ou biblioteca de terceiros; agora é built-in e hashable.
Antes de trocar dict por frozendict em código de produção, teste a serialização: bibliotecas que fazem duck typing e checks de tipo ( isinstance(x, dict) , type(x) is dict ) merecem um teste de caracterização, não uma suposição.
O sentinel substitui o padrão _MISSING = object() por algo que se lê bem em log e traceback. E UTF-8 como padrão em open() e fluxos de texto é a mudança mais traiçoeira no Windows: código que dependia de cp1252 para ler arquivo local passa a falhar ou a ler bytes errados. Onde o arquivo não é UTF-8, declare encoding="latin-1" explicitamente — o padrão novo não adivinha nada.
Tachyon e o JIT: o que dá e o que não dá para prometer
O Tachyon entra na biblioteca padrão como profiler por amostragem, com a vantagem de conseguir inspecionar um processo em execução sem o custo de um profiler determinístico. É a ferramenta certa para responder "onde o serviço queima CPU em produção" em vez de "quantas vezes essa função foi chamada". Vale medir antes de otimizar: os relatos de ganho com o JIT em 3.15 são de cargas específicas, e a própria comunidade discute o futuro do JIT — não trate os números de benchmark como promessa para o seu código.
Sobre ferramental, o suporte veio rápido: desde 3 de novembro o Dependabot reconhece 3.15 e o CodeQL 2.27.3 entende a sintaxe nova, incluindo os tipos embutidos. Antes de promover 3.15 como versão do runtime em CI, confira linters e type checkers (Ruff, ty, Pyrefly), disponibilidade de wheels para extensões C e imagens base de container.
Recomendação prática
Rode python -X importtime -c "import seu_app" e hyperfine no --help da sua CLI; se os imports dominam, o lazy import é o maior ganho isolado do release.
Adote lazy explicitamente, só nos imports de terceiros pesados e nos módulos de folha. Nunca em módulos que registram coisas na importação.
Trave o comportamento com um teste de fumaça que exercita cada entrypoint — ele substitui o fail-fast que você perdeu.
Faça o bump para 3.15 agora em projeto novo e em ferramenta interna; para serviço crítico com muitas dependências, valide em staging com a matriz de wheels fechada.
Atualize o piso do projeto: manter suporte a 3.10 não faz mais sentido desde o fim da vida, em 1º de outubro.
Se o seu time está decidindo entre atualizar a base existente ou reescrever módulos críticos, vale uma conversa com o time da Maquinaweb para revisar arquitetura, dependências e o caminho de migração antes de mexer no pyproject.toml .
Fontes
Continue lendo
Desenvolvimento Polícia Civil de SP: 22 mil credenciais à venda por US$ 40 mil, Barracuda com root explorado e Nitori com 37,6 milhões
Grupo bl33dr00t põe 22 mil credenciais da Polícia Civil de SP à venda por US$ 40 mil enquanto a SSP nega invasão. Na mesma janela, a Barracuda confirma exploração de falhas que dão root em gateways ESG, a CISA corre contra o prazo de 13/11 e a Nitori expõe até 37,6 milhões de registros.
Ler artigo →
Desenvolvimento RHEL 10.3 sela o sistema com criptografia e o EPYC 9006 chega a 256 núcleos: a base física dos agentes de IA
Red Hat libera RHEL 10.3 e 9.9 com imagens seladas, MCP server GA para diagnóstico assistido e GCC 16. No mesmo dia, a AMD põe à venda o EPYC 9006 (Zen 6) com até 256 núcleos. E o Paraná usa IA do Google para encurtar a busca por terapia oncológica.
Ler artigo →
Desenvolvimento N-central fora do ar no mundo inteiro: 33 zero-days derrubam o RMM que administra o resto
N-able tirou o N-central hospedado do ar em todo o mundo depois que um pesquisador publicou 33 zero-days com PoC e lançou hotfix com 7 CVEs. Na mesma janela, o Citrus usa VLESS e LLMs em ataques, e a Fireworks AI confirma vazamento de datasets e pesos de modelos.
Ler artigo →