Python 3.15: lazy imports, frozendict, UTF-8 por padrão e um JIT mais rápido

O Python 3.15.0 foi lançado em 9 de outubro de 2026. É uma daquelas versões em que quase toda área recebe algo: sintaxe nova, tipos embutidos novos, uma mudança de encoding padrão que vinha sendo anunciada há anos, um profiler de amostragem e mais um passo no JIT experimental. Passei pela página de release e pelo “What’s New” para separar o que mais afeta o código do dia a dia.

Lazy imports explícitos

A mudança mais visível é a PEP 810, que adiciona imports preguiçosos explícitos, descritos na página de release como uma forma de ter inicialização mais rápida. O exemplo da documentação:

lazy import json
lazy from pathlib import Path

print("Starting up...")  # json and pathlib are not fully imported yet

data = json.loads('{"key": "value"}')  # json gets fully imported here
p = Path(".")  # pathlib gets fully imported here

O módulo só é carregado de fato no primeiro uso. Para CLIs e ferramentas que importam muita coisa e usam pouco em cada execução, isso pode encurtar bastante o tempo de partida. A documentação também mostra controles globais via sys.set_lazy_imports e sys.set_lazy_imports_filter, e compatibilidade com uma variável __lazy_modules__ em nível de módulo, o que ajuda bibliotecas que precisam suportar versões anteriores.

Novos tipos embutidos e sintaxe

Dois tipos novos chegam aos builtins. O frozendict (PEP 814) é um dicionário imutável e hashable: tentar atribuir uma chave levanta TypeError, e dois frozendict com o mesmo conteúdo têm o mesmo hash, mesmo em ordem diferente. Finalmente dá para usar um dicionário como chave de outro dicionário sem gambiarras.

O sentinel (PEP 661) cria valores sentinela únicos com uma representação legível. Segundo a documentação, eles preservam identidade quando copiados, podem ser usados em expressões de tipo com o operador | e podem ser serializados com pickle quando importáveis por módulo e nome. É o fim do clássico _MISSING = object().

A PEP 798 permite desempacotamento dentro de comprehensions:

>>> lists = [[1, 2], [3, 4], [5]]
>>> [*L for L in lists]
[1, 2, 3, 4, 5]

Funciona também com sets, dicts (com **) e generator expressions.

Outra mudança com potencial de impacto: a PEP 686 torna UTF-8 o encoding padrão. Código que abria arquivos sem especificar encoding e dependia do padrão do sistema, especialmente no Windows, pode mudar de comportamento.

Desempenho e observabilidade

O JIT experimental recebeu uma atualização importante. Segundo a página de release, ele mostra uma melhoria de média geométrica de 7% a 8% sobre o interpretador padrão em x86-64 Linux e de 11% a 12% sobre o interpretador tail-calling em AArch64 macOS. Os binários oficiais de 64 bits para Windows passam a usar o interpretador tail-calling.

Para observabilidade, a PEP 831 liga frame pointers por padrão, e a PEP 799 cria o pacote profiling, que organiza os profilers em profiling.tracing (o antigo cProfile, que continua como alias) e profiling.sampling. Este último é o Tachyon, um profiler de amostragem que se conecta a processos em execução sem mudar código nem reiniciar, com saídas como flamegraph e formato do Firefox Profiler. O módulo profile foi descontinuado e deve sair no Python 3.17.

Tipagem, C API e ressalvas

Na tipagem, a PEP 728 permite TypedDict com itens extras tipados e a PEP 747 introduz TypeForm para anotar formas de tipo. Na C API, chegam o PyBytesWriter (PEP 782) e um ABI estável para builds free-threaded (PEPs 803, 820 e 793). Os instaladores oficiais de macOS agora incluem o suporte a free-threading por padrão.

Uma ressalva importante: no macOS 27.0, o IDLE e outros apps tkinter podem travar ao abrir diálogos a partir de menus. A página atribui o problema a uma mudança de comportamento do sistema que afeta as versões atuais do Tk e recomenda adiar o upgrade do macOS se você depende desses apps, ou testar antes.

Minha leitura

Lazy imports e frozendict são o tipo de recurso que a comunidade pediu por anos e que muda pequenas decisões de design todos os dias. O UTF-8 por padrão é a mudança que eu testaria primeiro numa base de código antiga, porque é silenciosa: nenhum erro aparece de imediato, só o conteúdo lido muda.

Quanto ao JIT, os números são encorajadores, mas continuam vindos do próprio projeto e de um recurso experimental. Eu mediria no meu workload antes de contar com eles. E se você usa Mac com macOS 27, leia a nota sobre o Tk antes de atualizar a máquina.

Fontes