Python Novidades do 3.14 e do 3.15

O Python ganha uma versão nova todo ano, em outubro. Este guia ensina o Python 3.14, lançado em 7 de outubro de 2025: é o que vem no Ubuntu 26.04 e o que roda no editor do guia. Um ano depois, em 9 de outubro de 2026, saiu o Python 3.15. Esta página reúne o que mudou nas duas versões, do jeito que interessa a quem está aprendendo: o que você vai ver de diferente, o que pode escrever agora e o que vai encontrar no código dos outros. Tudo foi conferido nas páginas oficiais What's New ("O que há de novo") em docs.python.org/3/whatsnew/; o que é do 3.14 foi rodado no Python 3.14 de verdade.

Você vai aprender
  • Como saber qual Python você tem, e como as versões são lançadas.
  • As novidades do 3.14 que você sente logo: mensagens de erro mais espertas e o modo interativo colorido.
  • except A, B: sem parênteses (PEP 758).
  • As template strings, t"…": parentes das f-strings, mas com um superpoder (PEP 750).
  • As anotações de tipo adiadas (PEP 649) e o aviso de return dentro do finally (PEP 765).
  • Melhorias pequenas e úteis: Path.copy(), map(strict=True), uuid7() e outras.
  • O que chegou no 3.15: lazy import, frozendict, desempacotamento em comprehensions, UTF-8 por padrão, dicas de erro novas.

A ideia: um celular que recebe atualizações

O seu celular de hoje faz o mesmo que fazia ontem, mas a cada atualização ganha alguma coisa: um atalho novo, uma mensagem mais clara, uma função que antes precisava de um aplicativo à parte. Raramente algo some, e quando some há aviso com antecedência. As versões do Python são assim: o código que você escreveu para o 3.13 continua rodando no 3.14 quase sempre, e as mudanças que quebram coisas são avisadas versões antes (com os DeprecationWarning, "aviso de obsolescência").

TermoO que é
3.14.43 = a "geração" do Python (desde 2008); 14 = a versão do ano; 4 = a quarta correção dela. As correções (3.14.1, 3.14.2…) só consertam defeitos, não trazem recursos
PEPPython Enhancement Proposal, "proposta de melhoria": o documento numerado que descreve cada mudança grande (veja PEP 8)
Ciclo de vidaCada versão recebe correções por cerca de cinco anos; a situação de cada uma está em devguide.python.org/versions

Qual é o meu Python?

import sys

print(sys.version)
print(sys.version_info)
print(sys.version_info >= (3, 14))
$ python3 --version
Python 3.14.4
$ python3 versao.py
3.14.4 (main, Aug 20 2026, 10:41:58) [GCC 15.2.0]
sys.version_info(major=3, minor=14, micro=4, releaselevel='final', serial=0)
True

O sys.version_info é uma tupla com nome, e compará-la com (3, 14) diz se o Python é o 3.14 ou mais novo. O editor do guia roda o 3.14 também (uma correção diferente da do terminal da Ana, o que não muda nada nesta página). E o Debian 13, por exemplo, traz o 3.13: as novidades abaixo não funcionam nele.


Python 3.14 3.14

1. Mensagens de erro mais espertas

Desde o 3.10, a cada versão o Python explica melhor os erros. No 3.14, ele passou a reconhecer palavras-chave digitadas errado:

whille True:
    pass
$ python3 erros_314.py
  File "/home/ana/erros_314.py", line 1
    whille True:
    ^^^^^^
SyntaxError: invalid syntax. Did you mean 'while'?

"Sintaxe inválida. Você quis dizer while?" Antes, era só invalid syntax. Outros casos novos, todos colhidos no 3.14:

nota = 7
if nota >= 9:
    print("Ótimo")
else:
    print("Continue estudando")
elif nota >= 6:
    print("Bom")
$ python3 erro_elif.py
  File "/home/ana/erro_elif.py", line 6
    elif nota >= 6:
    ^^^^
SyntaxError: 'elif' block follows an 'else' block

"Um bloco elif depois de um bloco else." O else é sempre o último (veja elif).

estoque = {}
produto = ["arroz", "5 kg"]
estoque[produto] = 12
$ python3 erro_chave.py
Traceback (most recent call last):
  File "/home/ana/erro_chave.py", line 3, in <module>
    estoque[produto] = 12
    ~~~~~~~^^^^^^^^^
TypeError: cannot use 'list' as a dict key (unhashable type: 'list')

"Não dá para usar uma list como chave de dicionário (tipo não hashable)." Antes vinha só unhashable type: 'list'; agora a mensagem diz o que você tentou fazer. Listas podem mudar, por isso não servem de chave; uma tupla serve: ("arroz", "5 kg"). O mesmo vale para os conjuntos:

$ python3 erro_conjunto.py
Traceback (most recent call last):
  File "/home/ana/erro_conjunto.py", line 2, in <module>
    vistos.add({"nome": "Ana"})
    ~~~~~~~~~~^^^^^^^^^^^^^^^^^
TypeError: cannot use 'dict' as a set element (unhashable type: 'dict')
$ python3 erro_prefixo.py
  File "/home/ana/erro_prefixo.py", line 1
    dados = ub"abc"
            ^^
SyntaxError: 'u' and 'b' prefixes are incompatible

"Não dá para usar um dict como elemento de conjunto", e, no segundo, "os prefixos u e b são incompatíveis" (um texto não pode ser ao mesmo tempo texto Unicode e bytes).

2. O modo interativo colorido

No terminal, o >>> do modo interativo agora colore o código enquanto você digita: o prompt em roxo, os textos em verde, os números, as palavras-chave e as funções embutidas cada um com sua cor. E o Tab completa nomes de módulos no import: digite import co e aperte Tab para ver os módulos que começam com "co". As cores também chegaram a outras ferramentas: o relatório do unittest (veja Testes), o código que o pdb mostra (veja Depuração), a ajuda do argparse, os tracebacks.

$ python3
Python 3.14.4 (main, Aug 20 2026, 10:41:58) [GCC 15.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> nome = "Ana"
>>> t"Olá, {nome}!"
Template(strings=('Olá, ', '!'), interpolations=(Interpolation('Ana', 'nome', None, ''),))
>>> exit()

(Aqui, sem as cores. Esse t"…" é a próxima novidade.) Se as cores atrapalharem, a variável de ambiente NO_COLOR=1 desliga todas.

3. except sem parênteses (PEP 758)

Para tratar dois tipos de erro do mesmo jeito, era obrigatório pôr os tipos entre parênteses: except (ValueError, ZeroDivisionError):. No 3.14, os parênteses podem sair:

texto = "dez"

try:
    quantidade = int(texto)
    porcao = 100 / quantidade
except ValueError, ZeroDivisionError:
    print("Quantidade inválida:", texto)
$ python3 except_sem_parenteses.py
Quantidade inválida: dez

Mas só quando não há o as. Com o as (para guardar o erro numa variável), os parênteses continuam obrigatórios, e o 3.14 diz isso com todas as letras:

try:
    quantidade = int("dez")
except ValueError, ZeroDivisionError as erro:
    print("Deu erro:", erro)
$ python3 except_as.py
  File "/home/ana/except_as.py", line 3
    except ValueError, ZeroDivisionError as erro:
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
SyntaxError: multiple exception types must be parenthesized when using 'as'

"Vários tipos de exceção precisam de parênteses quando se usa as." Conserto: except (ValueError, ZeroDivisionError) as erro:. (A aula try e except explica o try.)

Em Pythons mais antigos

Até o 3.13, except ValueError, ZeroDivisionError: é SyntaxError. Se o seu programa precisa rodar em versões mais velhas, continue com os parênteses: eles funcionam em todas.

Missão: tire os parênteses do except, no estilo novo do 3.14. O programa deve continuar mostrando a mensagem de erro.

precos = {"arroz": "27.90", "café": "dezoito"}

try:
    preco = float(precos["café"])
except (ValueError, KeyError):
    print("Produto ou preço inválido")
precos = {"arroz": "27.90", "café": "dezoito"}

try:
    preco = float(precos["café"])
except ValueError, KeyError:
    print("Produto ou preço inválido")

4. Template strings: t"…" (PEP 750)

As f-strings montam o texto na hora: f"Olá, {nome}!" vira "Olá, Ana!", e pronto, não dá mais para saber qual parte era fixa e qual veio da variável. A t-string (de template, "modelo") se escreve igual, com t no lugar do f, mas não monta o texto: ela guarda as partes separadas, para uma função decidir o que fazer com cada uma.

nome = "Ana"
fstring = f"Olá, {nome}!"
template = t"Olá, {nome}!"

print(type(fstring), fstring)
print(type(template))
print(list(template))
print(template.strings)
print(template.values)
$ python3 tstring.py
<class 'str'> Olá, Ana!
<class 'string.templatelib.Template'>
['Olá, ', Interpolation('Ana', 'nome', None, ''), '!']
('Olá, ', '!')
('Ana',)
t"Olá, {nome}!"
Uma t-string. O resultado não é um str: é um objeto Template, do módulo string.templatelib.
list(template)
Percorrer o template entrega as partes em ordem: os pedaços fixos (textos comuns) e as interpolações (as partes entre chaves).
Interpolation('Ana', 'nome', None, '')
Uma interpolação guarda o valor ('Ana'), o código que estava entre as chaves ('nome') e a formatação, se houver.
template.strings / template.values
Só os pedaços fixos / só os valores.

Para que serve? Para tratar de um jeito o que você escreveu e de outro o que veio de fora. O exemplo clássico é uma página de internet: se um visitante escreve um comentário com código HTML dentro, e o site cola o comentário direto na página, o código do visitante roda no navegador de todo mundo (é o "primo" do SQL injection). Com uma t-string, a função pode "desarmar" só os valores:

from html import escape
from string.templatelib import Interpolation


def html(template):
    partes = []
    for parte in template:
        if isinstance(parte, Interpolation):
            partes.append(escape(str(parte.value)))
        else:
            partes.append(parte)
    return "".join(partes)


comentario = "<script>roubar()</script> Adorei o bolo!"
print(f"<p>{comentario}</p>")
print(html(t"<p>{comentario}</p>"))
$ python3 tstring_html.py
<p><script>roubar()</script> Adorei o bolo!</p>
<p>&lt;script&gt;roubar()&lt;/script&gt; Adorei o bolo!</p>
  1. A função html() percorre as partes do template. As partes fixas (<p> e </p>, que você escreveu) passam do jeito que estão.
  2. As interpolações (o comentário, que veio do visitante) passam pelo escape() do módulo html, que troca < por &lt; e > por &gt;. O navegador mostra esses códigos como os sinais, mas não executa nada.
  3. Com a f-string, o <script> foi colado inteiro. Com a t-string, saiu desarmado, e o <p> que você escreveu continuou funcionando.

Uma t-string não é um texto: para mostrar, alguma função tem de transformá-la. Tentar usá-la como texto dá erro:

nome = "Ana"
template = t"Olá, {nome}!"
print(template)
print("Bem-vinda: " + template)
$ python3 tstring_str.py
Template(strings=('Olá, ', '!'), interpolations=(Interpolation('Ana', 'nome', None, ''),))
Traceback (most recent call last):
  File "/home/ana/tstring_str.py", line 4, in <module>
    print("Bem-vinda: " + template)
          ~~~~~~~~~~~~~~^~~~~~~~~~
TypeError: can only concatenate str (not "string.templatelib.Template") to str

O print mostrou a "ficha técnica" do template, e não "Olá, Ana!". E o + recusou: "só dá para juntar str com str (não com Template)". No dia a dia, para montar texto, continue com as f-strings. As t-strings são para quem escreve bibliotecas que precisam tratar os valores com cuidado (HTML, SQL, comandos, logs); você vai usá-las através dessas bibliotecas.

Missão: o nome do cliente veio com um código HTML (<b>). A função html() já sabe desarmar os valores, mas só funciona com uma t-string. Troque o f por t na linha do print e rode: o <b> do nome tem de sair como &lt;b&gt;.

from html import escape
from string.templatelib import Interpolation


def html(template):
    partes = []
    for parte in template:
        if isinstance(parte, Interpolation):
            partes.append(escape(str(parte.value)))
        else:
            partes.append(parte)
    return "".join(partes)


cliente = "<b>Ana</b>"
print(html(f"<p>Pedido de {cliente} saiu para entrega</p>"))
from html import escape
from string.templatelib import Interpolation


def html(template):
    partes = []
    for parte in template:
        if isinstance(parte, Interpolation):
            partes.append(escape(str(parte.value)))
        else:
            partes.append(parte)
    return "".join(partes)


cliente = "<b>Ana</b>"
print(html(t"<p>Pedido de {cliente} saiu para entrega</p>"))

Por que a versão com f não deu erro? Porque um texto comum também pode ser percorrido (letra por letra), e nenhuma letra é uma Interpolation: a função devolveu o texto sem desarmar nada. É exatamente o perigo que a t-string evita.

5. Anotações adiadas (PEP 649)

As dicas de tipo (anotações) dizem que tipo cada parâmetro espera. Até o 3.13, o Python calculava as anotações no momento do def; se uma anotação citava uma classe definida mais abaixo no arquivo, dava NameError, e o truque era escrevê-la entre aspas (-> "Cliente"). No 3.14, as anotações só são calculadas quando alguém pede por elas:

def cadastrar(nome: str) -> Cliente:
    return Cliente(nome)


class Cliente:
    def __init__(self, nome: str) -> None:
        self.nome = nome


ana = cadastrar("Ana")
print(ana.nome)
print(cadastrar.__annotations__)
$ python3 anotacoes.py
Ana
{'nome': <class 'str'>, 'return': <class '__main__.Cliente'>}

A função cadastrar foi criada antes de existir a classe Cliente, e não deu erro: quando o print pediu as anotações (__annotations__), a classe já existia. Para quem lê as anotações por programa, o módulo novo annotationlib pode entregá-las de três jeitos, inclusive quando um nome não existe:

from annotationlib import Format, get_annotations


def entregar(pedido: Pedido, endereco: str) -> None:
    pass


print(get_annotations(entregar, format=Format.STRING))
print(get_annotations(entregar, format=Format.FORWARDREF))
print(get_annotations(entregar))
$ python3 anotacoes_lib.py
{'pedido': 'Pedido', 'endereco': 'str', 'return': 'None'}
{'pedido': ForwardRef('Pedido', owner=<function entregar at 0x7fd70d37f320>), 'endereco': <class 'str'>, 'return': None}
Traceback (most recent call last):
  File "/home/ana/anotacoes_lib.py", line 10, in <module>
    print(get_annotations(entregar))
          ~~~~~~~~~~~~~~~^^^^^^^^^^
  File "/usr/lib/python3.14/annotationlib.py", line 966, in get_annotations
    ann = _get_dunder_annotations(obj)
  File "/usr/lib/python3.14/annotationlib.py", line 1159, in _get_dunder_annotations
    ann = getattr(obj, "__annotations__", None)
  File "/home/ana/anotacoes_lib.py", line 4, in __annotate__
    def entregar(pedido: Pedido, endereco: str) -> None:
                         ^^^^^^
NameError: name 'Pedido' is not defined
Format.STRING
As anotações como textos, exatamente como foram escritas. Nunca dá erro.
Format.FORWARDREF
Os valores reais quando existem (str, None) e uma "referência adiantada" (ForwardRef('Pedido')) para o que ainda não existe.
sem formato (VALUE)
Os valores reais. Como Pedido nunca foi definido, o cálculo atrasado deu NameError, e o traceback mostra a linha do def (dentro de uma função escondida chamada __annotate__).

Repare: o erro aconteceu na linha 10, quando as anotações foram pedidas, e não na linha 4, onde o def foi escrito. Esse é o "adiado". Para quem só escreve dicas de tipo, a mudança é simples: as aspas em volta de nomes definidos mais abaixo não são mais necessárias.

O __annotations__ do módulo sumiu

Uma consequência que pega quem vem de versões antigas: anotações de variáveis soltas no arquivo também são adiadas, e o nome __annotations__ não existe mais "solto" no módulo:

preco: float = 27.90
print(__annotations__)
$ python3 modulo_anot.py
Traceback (most recent call last):
  File "/home/ana/modulo_anot.py", line 2, in <module>
    print(__annotations__)
          ^^^^^^^^^^^^^^^
NameError: name '__annotations__' is not defined. Did you mean: '__annotate__'?

No 3.13, isso mostrava {'preco': <class 'float'>}. No 3.14, a dica aponta o __annotate__, a função escondida que calcula as anotações. Para lê-las, peça ao módulo: sys.modules[__name__].__annotations__ (com import sys) ou annotationlib.get_annotations() passando o módulo.

6. return dentro do finally agora dá aviso (PEP 765)

O bloco finally roda sempre, com erro ou sem erro (veja else e finally). Um return lá dentro "engole" qualquer erro, e o programa segue como se nada tivesse acontecido. O 3.14 passou a avisar:

def dividir(a, b):
    try:
        return a / b
    finally:
        return "acabou"


print(dividir(10, 0))
$ python3 finally_return.py
/home/ana/finally_return.py:5: SyntaxWarning: 'return' in a 'finally' block
  return "acabou"
acabou

Dividir por zero devia dar ZeroDivisionError, mas o return do finally jogou o erro fora e a função devolveu "acabou". O SyntaxWarning ("aviso de sintaxe": não para o programa) aponta o problema. O mesmo aviso vale para break e continue que saem de um finally. Conserto: tire o return do finally; ele serve para "arrumar a casa" (fechar arquivos, por exemplo), não para devolver valores.

7. Pequenas melhorias úteis

from pathlib import Path

lista = Path("compras.txt")
lista.write_text("arroz\nfeijão\n", encoding="utf-8")

copia = lista.copy("compras-backup.txt")
print(copia, copia.read_text(encoding="utf-8").split())

pasta = Path("antigas")
pasta.mkdir(exist_ok=True)
movida = copia.move_into(pasta)
print(movida, movida.exists(), copia.exists())
$ python3 pathlib_copia.py
compras-backup.txt ['arroz', 'feijão']
antigas/compras-backup.txt True False

O pathlib ganhou copy() (copiar), copy_into() (copiar para dentro de uma pasta), move() (mover) e move_into() (mover para dentro de uma pasta). Antes, era preciso o módulo shutil. Repare na última linha: depois de mover, o arquivo existe na pasta nova (True) e não mais no lugar antigo (False).

precos = [27.90, 8.49, 4.99]
quantidades = [2, 3]

print(list(map(lambda p, q: p * q, precos, quantidades)))
print(list(map(lambda p, q: p * q, precos, quantidades, strict=True)))
$ python3 map_strict.py
[55.8, 25.47]
Traceback (most recent call last):
  File "/home/ana/map_strict.py", line 5, in <module>
    print(list(map(lambda p, q: p * q, precos, quantidades, strict=True)))
          ~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ValueError: map() argument 2 is shorter than argument 1

Três preços e só duas quantidades: o map() comum para calado no menor (o açúcar sumiu da conta). Com strict=True ("estrito"), novo no 3.14, ele avisa: "o argumento 2 é mais curto que o argumento 1". O zip() já tinha essa opção desde o 3.10.

x = 1234.567891
print(f"{x:,.6f}")
print(f"{x:,.6_f}")
print(f"{x:_._f}")
$ python3 formatar.py
1,234.567891
1,234.567_891
1_234.567_891

Na formatação, o separador de milhares (, ou _) agora também pode agrupar as casas decimais, de três em três: basta repeti-lo depois do ponto.

Novidade do 3.14Em uma frase
uuid.uuid7()Identificadores únicos que já vêm em ordem de criação (bons para números de pedido); também uuid6() e uuid8()
compression.zstdUm módulo novo para o formato de compressão Zstandard (PEP 784)
concurrent.interpretersVários interpretadores Python no mesmo programa, rodando em paralelo (PEP 734)
Python sem GIL (free-threaded)A versão do Python que roda threads realmente em paralelo passou a ser oficialmente suportada, mas continua opcional (PEP 779)
python3 -m pdb -p PIDConectar o depurador a um programa que já está rodando (PEP 768); veja Depuração
unittestSaída colorida e conferências novas, como assertStartsWith; veja Testes
sqlite3Marcadores com nome (:nome) com uma tupla viraram erro; veja sqlite3
python3 -cO código passado ao -c agora pode vir indentado: o Python tira o recuo comum

Python 3.15 3.15

Exemplos ilustrativos

O Python deste guia (no terminal da Ana e no editor) é o 3.14, que não entende o que vem abaixo. Os exemplos com >>> desta seção foram copiados da página oficial What's New in Python 3.15 (docs.python.org/3.15/whatsnew/3.15.html) e não foram rodados aqui. As saídas que aparecem com $ python3 são do 3.14 de verdade, mostrando o que acontece quando se tenta usar a novidade numa versão que ainda não a tem.

1. lazy import: importar só quando usar (PEP 810)

Um programa grande pode demorar para abrir só por causa dos imports: cada módulo é lido e executado no começo, mesmo que o programa nunca o use naquela vez. No 3.15, a palavra lazy ("preguiçoso") adia o carregamento até o primeiro uso:

lazy import json
lazy from pathlib import Path

print("Abrindo...")             # json e pathlib ainda não foram carregados
dados = json.loads('{"a": 1}')  # o json é carregado aqui

Os imports continuam no topo do arquivo, como pede a PEP 8, mas o custo só é pago quando o nome é usado. O lazy só vale no "corpo" do módulo (dentro de uma função ou de um try, é SyntaxError). No 3.14, a palavra nem existe:

$ python3 so315c.py
  File "/home/ana/so315c.py", line 1
    lazy import json
         ^^^^^^
SyntaxError: invalid syntax

2. frozendict: o dicionário que não muda (PEP 814)

Assim como o frozenset é o conjunto que não muda (veja Conjuntos), o frozendict é o dicionário congelado: depois de criado, não aceita chaves novas nem mudanças. Em troca, pode ser chave de outro dicionário ou item de um conjunto. Da documentação:

>>> a = frozendict(x=1, y=2)
>>> a
frozendict({'x': 1, 'y': 2})
>>> a['z'] = 3
Traceback (most recent call last):
  File "<python-input-2>", line 1, in <module>
    a['z'] = 3
    ~^^^^^
TypeError: 'frozendict' object does not support item assignment
>>> b = frozendict(y=2, x=1)
>>> hash(a) == hash(b)
True
>>> a == b
True

E no 3.14, o nome não existe (repare na dica do Python, que sugere o parente mais próximo):

$ python3 so315b.py
Traceback (most recent call last):
  File "/home/ana/so315b.py", line 1, in <module>
    config = frozendict(tema="escuro", fonte=14)
             ^^^^^^^^^^
NameError: name 'frozendict' is not defined. Did you mean: 'frozenset'?

3. * e ** dentro das comprehensions (PEP 798)

Para "achatar" uma lista de listas numa lista só, era preciso uma comprehension com dois for. No 3.15, o * de desempacotar pode entrar na comprehension:

>>> lists = [[1, 2], [3, 4], [5]]
>>> [*L for L in lists]  # equivalent to [x for L in lists for x in L]
[1, 2, 3, 4, 5]
>>> dicts = [{'a': 1}, {'b': 2}, {'a': 3}]
>>> {**d for d in dicts}  # equivalent to {k: v for d in dicts for k,v in d.items()}
{'a': 3, 'b': 2}

No 3.14, isso é um erro com mensagem própria:

$ python3 so315.py
  File "/home/ana/so315.py", line 2
    print([*l for l in listas])
           ^^
SyntaxError: iterable unpacking cannot be used in comprehension

"Desempacotamento não pode ser usado numa comprehension." No 3.14, use a forma com dois for: [x for l in listas for x in l].

4. UTF-8 por padrão (PEP 686)

Quando você abre um arquivo de texto sem dizer o encoding (a "tabela" que transforma letras em bytes), o Python usava a do sistema. No Linux e no macOS quase sempre é UTF-8, mas no Windows podia ser outra, e um arquivo com "ç" e "ã" gravado num computador saía embaralhado no outro. No 3.15, open("compras.txt") usa UTF-8 em qualquer sistema. Mesmo assim, a recomendação continua: escreva encoding="utf-8", como nas aulas de arquivos, e o programa funciona igual em todas as versões.

5. Mais dicas nas mensagens de erro

Quem vem de outra linguagem costuma usar o nome de método de lá. No 3.14, a mensagem só diz que o método não existe:

$ python3 so315d.py
Traceback (most recent call last):
  File "/home/ana/so315d.py", line 2, in <module>
    compras.push("feijão")
    ^^^^^^^^^^^^
AttributeError: 'list' object has no attribute 'push'

No 3.15, o Python reconhece nomes comuns do JavaScript, do Java, do Ruby e do C#, e sugere o equivalente. Da documentação:

>>> [1, 2, 3].push(4)
AttributeError: 'list' object has no attribute 'push'. Did you mean '.append'?
>>> 'hello'.toUpperCase()
AttributeError: 'str' object has no attribute 'toUpperCase'. Did you mean '.upper'?
>>> {}.put("a", 1)
AttributeError: 'dict' object has no attribute 'put'. Use d[k] = v.
>>> (1, 2, 3).append(4)
AttributeError: 'tuple' object has no attribute 'append'. Did you mean to use a 'list' object?

6. O resto, em uma frase

Novidade do 3.15Em uma frase
sentinel (PEP 661)Um tipo embutido para criar "valores marcadores" únicos, como um None próprio que quer dizer "não informado"
profiling e o Tachyon (PEP 799)Um pacote novo para medir onde o programa gasta tempo, com um medidor por amostragem rápido
math.integerUm módulo novo com as funções matemáticas para números inteiros (PEP 791)
Mais coresA ajuda do python3 --help, o argparse, o calendar, o timeit, a linha de comando do sqlite3 (que também completa SQL com Tab) e outras
Modo interativoO Tab colore as sugestões pelo tipo de objeto e sugere os nomes de dentro de um módulo no from … import
pdbO (Pdb) usa o mesmo editor de linha do modo interativo novo
JITO compilador experimental "na hora" (just-in-time), que acelera o Python, foi bastante melhorado
sqlite3.connect()Todos os parâmetros, menos o nome do banco, passam a ser só nomeados (timeout=5, e não um 5 solto)

Formas de lidar com versões diferentes

SituaçãoO que fazer
Você está aprendendoUse a versão que o seu sistema traz (no Ubuntu 26.04, o 3.14) e não se preocupe
O programa vai rodar em computadores com Pythons diferentesEscreva no estilo da versão mais velha que precisa funcionar (por exemplo, except (A, B): com parênteses)
Quer usar um recurso novo e avisar quem tiver Python velhoif sys.version_info < (3, 14): sys.exit("Este programa precisa do Python 3.14")
Quer experimentar o 3.15 sem trocar o do sistemaInstale-o à parte, sem mexer no python3 do sistema (veja Instalar o Python), e use um ambiente virtual com ele
Quer saber o que mudou numa versãoLeia a página What's New dela em docs.python.org/3/whatsnew/

Quando dá errado

Código novo num Python velho

O erro mais comum com novidades é rodar o código numa versão que não as tem. Os sinais: SyntaxError: invalid syntax numa linha que parece certa (lazy import, except A, B: no 3.13), ou NameError para um nome que a documentação garante que existe (frozendict no 3.14). Antes de procurar o erro no código, confira a versão: python3 --version.

as sem parênteses no except

A novidade do 3.14 tem um limite: except ValueError, ZeroDivisionError as erro: dá SyntaxError: multiple exception types must be parenthesized when using 'as', como você viu na seção 3. Com as, parênteses: except (ValueError, ZeroDivisionError) as erro:.

Tratar uma t-string como texto

print(t"…") mostra Template(strings=…), e "texto" + t"…" dá TypeError: can only concatenate str (not "string.templatelib.Template") to str (seção 4). Uma t-string precisa de uma função que a transforme. Se você só quer montar um texto, a ferramenta é a f-string.

Missão: este programa tem três erros, e as mensagens do Python 3.14 apontam cada um. Rode, leia a mensagem, conserte e rode de novo, até o fim. (Dica: palavra-chave digitada errado, a ordem do elif e uma lista que não pode ser chave de dicionário.)

contador = 0
whille contador < 3:
    contador += 1

nota = 7
if nota >= 9:
    print("Ótimo")
else:
    print("Continue estudando")
elif nota >= 6:
    print("Bom")

estoque = {}
produto = ["arroz", "5 kg"]
estoque[produto] = 12
print("Fim:", contador, estoque)
contador = 0
while contador < 3:
    contador += 1

nota = 7
if nota >= 9:
    print("Ótimo")
elif nota >= 6:
    print("Bom")
else:
    print("Continue estudando")

estoque = {}
produto = ("arroz", "5 kg")
estoque[produto] = 12
print("Fim:", contador, estoque)

Referência rápida

RecursoVersãoExemplo
except sem parênteses3.14except ValueError, KeyError:
Template strings3.14t"Olá, {nome}!" → Template
Anotações adiadas3.14def f() -> Cliente: antes de class Cliente; annotationlib.get_annotations()
Aviso de return/break/continue no finally3.14SyntaxWarning: 'return' in a 'finally' block
Copiar e mover com pathlib3.14Path("a.txt").copy("b.txt"), .move_into(pasta)
map(..., strict=True)3.14Erro se as sequências tiverem tamanhos diferentes
Modo interativo colorido, Tab no import3.14python3
lazy import3.15lazy import json
frozendict3.15frozendict(x=1, y=2)
*/** em comprehensions3.15[*l for l in listas]
UTF-8 por padrão3.15open("a.txt") lê e grava em UTF-8
Versão em usotodaspython3 --version, sys.version_info >= (3, 14)

Exercícios

1. A letra da t-string

Uma f-string começa com f. Com que letra começa uma template string?

"Olá, {nome}!"

2. Qual Python?

Complete o comando que mostra a versão do Python no terminal:

python3

3. No Python 3.14, qual destas linhas é SyntaxError?

4. O que t"Olá, {nome}!" produz?

5. No Python 3.14, uma função anotada com -> Cliente, escrita antes da class Cliente:

6. Você roda config = frozendict(tema="escuro") e recebe NameError: name 'frozendict' is not defined. A causa mais provável:


Resumo

  • Sai um Python novo todo outubro. O deste guia é o 3.14 (outubro de 2025); o 3.15 saiu em 9 de outubro de 2026. Confira a sua com python3 --version.
  • 3.14: erros mais claros (Did you mean 'while'?, 'elif' block follows an 'else' block, cannot use 'list' as a dict key) e modo interativo colorido.
  • 3.14: except A, B: sem parênteses, mas com as os parênteses continuam.
  • 3.14: t"…" cria um Template, que guarda os valores separados para uma função tratá-los com cuidado; para montar texto, continue com f-strings.
  • 3.14: anotações adiadas (nada de aspas para nomes definidos depois), aviso de return no finally, Path.copy()/move(), map(strict=True).
  • 3.15: lazy import, frozendict, [*l for l in listas], UTF-8 por padrão e dicas como Did you mean '.append'?. Num Python mais velho, eles dão SyntaxError ou NameError.