Introdução aos tipos Python¶
🌐 Tradução por IA e humanos
Esta tradução foi feita por IA orientada por humanos. 🤝
Ela pode conter erros de interpretação do significado original ou soar pouco natural, etc. 🤖
Você pode melhorar esta tradução ajudando-nos a orientar melhor o LLM de IA.
O Python possui suporte para "anotações de tipo" opcionais (também chamadas de "type annotations").
Essas "anotações de tipo" ou anotações são uma sintaxe especial que permite declarar o tipo de uma variável.
Ao declarar tipos para suas variáveis, editores e ferramentas podem oferecer um melhor suporte.
Este é apenas um tutorial rápido / atualização sobre anotações de tipo do Python. Ele cobre apenas o mínimo necessário para usá-las com o FastAPI... que é realmente muito pouco.
O FastAPI é todo baseado nessas anotações de tipo, elas oferecem muitas vantagens e benefícios.
Mas mesmo que você nunca use o FastAPI, você se beneficiaria de aprender um pouco sobre elas.
Nota
Se você é um especialista em Python e já sabe tudo sobre anotações de tipo, pule para o próximo capítulo.
Motivação¶
Vamos começar com um exemplo simples:
defget_full_name(first_name, last_name):
full_name = first_name.title() + " " + last_name.title()
return full_name
print(get_full_name("john", "doe"))
A chamada deste programa gera:
John Doe
A função faz o seguinte:
- Pega um
first_nameelast_name. - Converte a primeira letra de cada uma em maiúsculas com
title(). - Concatena-os com um espaço no meio.
defget_full_name(first_name, last_name):
full_name = first_name.title() + " " + last_name.title()
return full_name
print(get_full_name("john", "doe"))
Edite-o¶
É um programa muito simples.
Mas agora imagine que você estava escrevendo do zero.
Em algum momento você começa a definir a função, e já tem os parâmetros prontos...
Mas então você tem que chamar "esse método que converte a primeira letra em maiúscula".
Era upper? Era uppercase? first_uppercase? capitalize?
Então, você tenta com o velho amigo do programador, o preenchimento automático do editor.
Você digita o primeiro parâmetro da função, first_name, depois um ponto (.) e, em seguida, pressiona Ctrl+Space para acionar o preenchimento.
Mas, infelizmente, você não obtém nada útil:
Adicionar tipos¶
Vamos modificar uma única linha da versão anterior.
Vamos mudar exatamente esse fragmento, os parâmetros da função, de:
first_name, last_name
para:
first_name: str, last_name: str
É isso aí.
Essas são as "anotações de tipo":
defget_full_name(first_name: str, last_name: str):
full_name = first_name.title() + " " + last_name.title()
return full_name
print(get_full_name("john", "doe"))
Isso não é o mesmo que declarar valores padrão como seria com:
first_name="john", last_name="doe"
É uma coisa diferente.
Estamos usando dois pontos (:), não sinal de igual (=).
E adicionar anotações de tipo normalmente não muda o que acontece do que aconteceria sem elas.
Mas agora, imagine que você está novamente no meio da criação dessa função, mas com anotações de tipo.
No mesmo ponto, você tenta acionar o autocompletar com Ctrl+Space e vê:
Com isso, você pode rolar, vendo as opções, até encontrar o que "soa familiar":
Mais motivação¶
Verifique esta função, ela já possui anotações de tipo:
defget_name_with_age(name: str, age: int):
name_with_age = name + " is this old: " + age
return name_with_age
Como o editor conhece os tipos das variáveis, você não obtém apenas o preenchimento, mas também as verificações de erro:
Agora você sabe que precisa corrigi-la, convertendo age em uma string com str(age):
defget_name_with_age(name: str, age: int):
name_with_age = name + " is this old: " + str(age)
return name_with_age
Declarando tipos¶
Você acabou de ver o local principal para declarar anotações de tipo. Como parâmetros de função.
Este também é o principal local em que você as usaria com o FastAPI.
Tipos simples¶
Você pode declarar todos os tipos padrão de Python, não apenas str.
Você pode usar, por exemplo:
intfloatboolbytes
defget_items(item_a: str, item_b: int, item_c: float, item_d: bool, item_e: bytes):
return item_a, item_b, item_c, item_d, item_e
Módulo typing¶
Para alguns casos de uso adicionais, você pode precisar importar alguns itens do módulo padrão typing, por exemplo, quando quiser declarar que algo pode ter "qualquer tipo", você pode usar Any de typing:
fromtypingimport Any
defsome_function(data: Any):
print(data)
Tipos genéricos¶
Alguns tipos podem receber "parâmetros de tipo" entre colchetes, para definir seus tipos internos, por exemplo, uma "list de strings" seria declarada como list[str].
Esses tipos que podem receber parâmetros de tipo são chamados tipos genéricos ou genéricos.
Você pode usar os mesmos tipos embutidos como genéricos (com colchetes e tipos dentro):
listtuplesetdict
List¶
Por exemplo, vamos definir uma variável para ser uma list de str.
Declare a variável, com a mesma sintaxe com dois pontos (:).
Como o tipo, coloque list.
Como a list é um tipo que contém alguns tipos internos, você os coloca entre colchetes:
defprocess_items(items: list[str]):
for item in items:
print(item)
Nota
Esses tipos internos dentro dos colchetes são chamados de "parâmetros de tipo".
Neste caso, str é o parâmetro de tipo passado para list.
Isso significa: "a variável items é uma list, e cada um dos itens desta list é uma str".
Ao fazer isso, seu editor pode fornecer suporte mesmo durante o processamento de itens da list:
Sem tipos, isso é quase impossível de alcançar.
Observe que a variável item é um dos elementos da list items.
E, ainda assim, o editor sabe que é um str e fornece suporte para isso.
Tuple e Set¶
Você faria o mesmo para declarar tuples e sets:
defprocess_items(items_t: tuple[int, int, str], items_s: set[bytes]):
return items_t, items_s
Isso significa:
- A variável
items_té umatuplecom 3 itens, umint, outrointe umastr. - A variável
items_sé umset, e cada um de seus itens é do tipobytes.
Dict¶
Para definir um dict, você passa 2 parâmetros de tipo, separados por vírgulas.
O primeiro parâmetro de tipo é para as chaves do dict.
O segundo parâmetro de tipo é para os valores do dict:
defprocess_items(prices: dict[str, float]):
for item_name, item_price in prices.items():
print(item_name)
print(item_price)
Isso significa:
- A variável
pricesé umdict:- As chaves deste
dictsão do tipostr(digamos, o nome de cada item). - Os valores deste
dictsão do tipofloat(digamos, o preço de cada item).
- As chaves deste
Union¶
Você pode declarar que uma variável pode ser de qualquer um dentre vários tipos, por exemplo, um int ou um str.
Para defini-la, você usa a barra vertical (|) para separar ambos os tipos.
Isso é chamado de "união", porque a variável pode ser qualquer coisa na união desses dois conjuntos de tipos.
defprocess_item(item: int | str):
print(item)
Isso significa que item pode ser um int ou um str.
Possivelmente None¶
Você pode declarar que um valor pode ter um tipo, como str, mas que ele também pode ser None.
defsay_hi(name: str | None = None):
if name is not None:
print(f"Hey {name}!")
else:
print("Hello World")
Usar str | None em vez de apenas str permitirá que o editor o ajude a detectar erros em que você poderia estar assumindo que um valor é sempre um str, quando na verdade ele também pode ser None.
Classes como tipos¶
Você também pode declarar uma classe como o tipo de uma variável.
Digamos que você tenha uma classe Person, com um nome:
classPerson:
def__init__(self, name: str):
self.name = name
defget_person_name(one_person: Person):
return one_person.name
Então você pode declarar que uma variável é do tipo Person:
classPerson:
def__init__(self, name: str):
self.name = name
defget_person_name(one_person: Person):
return one_person.name
E então, novamente, você recebe todo o suporte do editor:
Perceba que isso significa que "one_person é uma instância da classe Person".
Isso não significa que "one_person é a classe chamada Person".
Modelos Pydantic¶
Pydantic é uma biblioteca Python para executar a validação de dados.
Você declara a "forma" dos dados como classes com atributos.
E cada atributo tem um tipo.
Em seguida, você cria uma instância dessa classe com alguns valores e ela validará os valores, os converterá para o tipo apropriado (se for esse o caso) e fornecerá um objeto com todos os dados.
E você recebe todo o suporte do editor com esse objeto resultante.
Um exemplo da documentação oficial do Pydantic:
fromdatetimeimport datetime
frompydanticimport BaseModel
classUser(BaseModel):
id: int
name: str = "John Doe"
signup_ts: datetime | None = None
friends: list[int] = []
external_data = {
"id": "123",
"signup_ts": "2017年06月01日 12:22",
"friends": [1, "2", b"3"],
}
user = User(**external_data)
print(user)
# > User id=123 name='John Doe' signup_ts=datetime.datetime(2017, 6, 1, 12, 22) friends=[1, 2, 3]
print(user.id)
# > 123
Nota
Para saber mais sobre Pydantic, verifique a documentação.
O FastAPI é todo baseado em Pydantic.
Você verá muito mais disso na prática no Tutorial - Guia do usuário.
Anotações de Tipo com Anotações de Metadados¶
O Python também possui uma funcionalidade que permite incluir metadados adicionais nessas anotações de tipo utilizando Annotated.
Você pode importar Annotated de typing.
fromtypingimport Annotated
defsay_hello(name: Annotated[str, "this is just metadata"]) -> str:
return f"Hello {name}"
O Python em si não faz nada com este Annotated. E para editores e outras ferramentas, o tipo ainda é str.
Mas você pode utilizar este espaço dentro do Annotated para fornecer ao FastAPI metadados adicionais sobre como você deseja que a sua aplicação se comporte.
O importante aqui de se lembrar é que o primeiro parâmetro de tipo que você informar ao Annotated é o tipo de fato. O resto é apenas metadados para outras ferramentas.
Por hora, você precisa apenas saber que o Annotated existe, e que ele é Python padrão. 😎
Mais tarde você verá o quão poderoso ele pode ser.
Dica
O fato de que isso é Python padrão significa que você ainda obtém a melhor experiência de desenvolvedor possível no seu editor, com as ferramentas que você utiliza para analisar e refatorar o seu código, etc. ✨
E também que o seu código será muito compatível com diversas outras ferramentas e bibliotecas Python. 🚀
Anotações de tipo no FastAPI¶
O FastAPI aproveita essas anotações de tipo para fazer várias coisas.
Com o FastAPI, você declara parâmetros com anotações de tipo e obtém:
- Suporte ao editor.
- Verificações de tipo.
... e o FastAPI usa as mesmas declarações para:
- Definir requisitos: dos parâmetros de path da request, parâmetros da query, cabeçalhos, corpos, dependências, etc.
- Converter dados: da request para o tipo necessário.
- Validar dados: provenientes de cada request:
- Gerando erros automáticos retornados ao cliente quando os dados são inválidos.
- Documentar a API usando OpenAPI:
- que é usada pelas interfaces de usuário da documentação interativa automática.
Tudo isso pode parecer abstrato. Não se preocupe. Você verá tudo isso em ação no Tutorial - Guia do usuário.
O importante é que, usando tipos padrão de Python, em um único local (em vez de adicionar mais classes, decoradores, etc.), o FastAPI fará muito trabalho para você.
Nota
Se você já passou por todo o tutorial e voltou para ver mais sobre os tipos, um bom recurso é a "cheat sheet" do mypy.