Python & AI Tutorials Logo
Programação Python

42. Uma Introdução Suave a Type Hints (Opcional)

Ao longo deste livro, você escreveu código Python sem especificar quais tipos de dados suas variáveis armazenam ou quais tipos suas funções aceitam e retornam. Python funcionou perfeitamente bem assim — é uma linguagem dinamicamente tipada (dynamically typed), o que significa que os tipos são determinados em tempo de execução conforme seu programa roda. Essa flexibilidade é uma das maiores forças do Python, permitindo que você escreva código de forma rápida e expressiva.

No entanto, conforme os programas ficam maiores e mais complexos, essa flexibilidade às vezes pode tornar o código mais difícil de entender e manter. Quando você vê uma função como def process_data(items):, você pode se perguntar: Que tipo de dados items contém? Uma lista de strings? Um dicionário? Algo totalmente diferente?

Type hints (também chamados de type annotations) fornecem uma forma de documentar os tipos esperados no seu código. Eles são adições opcionais ao Python que podem deixar seu código mais claro, ajudar a detectar erros mais cedo e habilitar recursos poderosos de IDE — tudo isso sem mudar como o Python de fato executa seu código.

Este capítulo introduz type hints de forma suave, mostrando o que eles são, por que existem e como usá-los de modo eficaz. Como type hints são opcionais e não afetam como o Python executa seu código, este capítulo inteiro está marcado como opcional. Você pode escrever excelentes programas em Python sem nunca usar type hints. Mas entendê-los vai ajudar você a ler código Python moderno e decidir quando eles podem beneficiar seus próprios projetos.

42.1) Por que type hints foram adicionados ao Python

Python foi projetado como uma linguagem dinamicamente tipada desde o começo. Por décadas, programadores Python escreveram código sem qualquer informação de tipos, e isso funcionou maravilhosamente para incontáveis projetos. Então por que type hints foram adicionados ao Python em 2015 (com Python 3.5)?

O desafio de grandes bases de código

Conforme Python ficou mais popular para aplicações em larga escala, equipes encontraram desafios:

python
# Em uma grande base de código, o que esta função espera e retorna?
def calculate_discount(customer, items, code):
    # ... 50 linhas de código ...
    return result

Sem ler o corpo inteiro da função ou sua documentação, você não consegue dizer:

  • customer é um dicionário, um objeto customizado, ou outra coisa?
  • items é uma lista, uma tupla, ou um conjunto?
  • Que tipo é code — uma string, um inteiro?
  • O que a função retorna — um número, um dicionário, ou talvez None?

Em programas pequenos, essa ambiguidade é gerenciável. Você pode facilmente olhar como a função é usada em outros lugares. Mas em uma base de código com milhares de funções distribuídas por dezenas de arquivos, isso fica difícil.

A solução: Type hints opcionais

Os criadores do Python decidiram adicionar um sistema opcional para documentar tipos. A palavra-chave é "opcional" — type hints são totalmente voluntários. Você pode usá-los quando ajudarem, ignorá-los quando não ajudarem e misturar código anotado e não anotado livremente.

Aqui vai um exemplo simples para mostrar a sintaxe básica:

python
# Sem type hints
def add(a, b):
    return a + b
 
# Com type hints
def add(a: int, b: int) -> int:
    return a + b

A sintaxe é direta:

  • Dois-pontos (:) depois de um parâmetro mostra qual tipo ele deveria ser: a: int
  • Seta (->) antes dos dois-pontos mostra qual tipo a função retorna: -> int

Agora vamos ver isso com nosso exemplo anterior:

python
def calculate_discount(customer: dict, items: list, code: str) -> float:
    # ... 50 linhas de código ...
    return result

Agora fica imediatamente claro: customer é um dicionário, items é uma lista, code é uma string, e a função retorna um float.

Não se preocupe se essa sintaxe parecer desconhecida — vamos explorá-la em detalhes nas seções 42.3-42.6. Por enquanto, apenas perceba como você consegue ver de relance o que a função espera e o que ela retorna.

Com ou sem type hints, a função funciona exatamente do mesmo jeito — Python não verifica esses tipos em tempo de execução. (Vamos explorar esse ponto importante em detalhes na seção 42.2)

Uma abordagem gradual e pragmática

O sistema de type hints do Python foi projetado para ser:

  1. Opcional: você nunca é obrigado a usar type hints
  2. Gradual: você pode adicionar hints a algumas partes do seu código e não a outras
  3. Não intrusivo: hints não mudam como o Python executa seu código
  4. Amigável a ferramentas: ferramentas externas podem verificar hints, mas o próprio Python os ignora em tempo de execução

Essa abordagem pragmática permite que o Python continue flexível enquanto oferece benefícios para quem os quiser.

Não, obrigado

Sim, por favor

Código Python

Adicionar Type Hints?

✓ Funciona perfeitamente
✓ Mantenha simples

Adicionar Type Hints

✓ Mesmo comportamento em tempo de execução

✓ Melhor suporte de IDE

✓ Detectar erros mais cedo

42.2) A regra de ouro: sem verificação em tempo de execução

A coisa mais importante para entender sobre type hints é isto: o Python não verifica type hints em tempo de execução. Eles são puramente informativos. Vamos ver o que essa realidade chocante significa na prática.

Type hints não impedem tipos incorretos

Considere esta função com type hints:

python
def greet(name: str) -> str:
    return f"Hello, {name}!"
 
# Isso funciona bem, mesmo que 42 não seja uma string
result = greet(42)
print(result)  # Output: Hello, 42!

O type hint diz claramente que name deveria ser uma string, mas Python aceita alegremente o inteiro 42 e executa a função. Python não verifica o type hint — ele só usa o valor que você fornece.

Isso é fundamentalmente diferente de linguagens como Java ou C++, onde o compilador verifica tipos antes de executar seu código e se recusa a rodar se houver incompatibilidade de tipos. A abordagem do Python é mais permissiva: ele confia que você vai fornecer os tipos certos, mas não força isso.

O problema: os riscos da tipagem dinâmica continuam

Aqui está o verdadeiro desafio: mesmo com type hints, a tipagem dinâmica do Python significa que você ainda pode cometer erros de tipo que só aparecem em runtime:

python
def calculate_total(prices: list) -> float:
    """Calculate the sum of prices."""
    return sum(prices)
 
# Isso funciona bem
print(calculate_total([10.99, 5.50, 3.25]))  # Output: 19.74
 
# Mas isso falha em runtime!
print(calculate_total("not a list"))  # TypeError: 'str' object is not iterable

O type hint diz claramente que prices deveria ser uma lista, mas Python não impede você de passar uma string. O erro só aparece quando o código realmente roda e tenta usar sum() na string.

Isso é frustrante! Nós adicionamos type hints para pegar esses problemas, mas os riscos da tipagem dinâmica ainda estão ali. Erros de tipo podem ficar escondidos no seu código até o runtime, potencialmente aparecendo em produção quando um usuário faz algo inesperado.

Então, se type hints não impedem erros em runtime, qual é o sentido de usá-los?

Então para que servem type hints?

Type hints podem não mudar o comportamento em runtime do Python, mas eles cumprem um propósito crucial — eles fornecem informação para humanos e ferramentas, não para o próprio Python:

  1. Documentação: eles dizem quais tipos uma função espera e retorna
  2. Suporte de IDE: seu editor pode usar hints para fornecer autocomplete e mostrar avisos
  3. Análise estática: ferramentas externas (como mypy) podem verificar seu código em busca de erros de tipo antes de você rodar
  4. Entendimento do código: eles tornam grandes bases de código mais fáceis de ler e manter

Pense em type hints como comentários que ferramentas conseguem entender. Eles não mudam como o Python roda, mas ajudam você a escrever código melhor.

Mas como isso realmente ajuda a pegar aqueles erros em runtime que acabamos de ver?

A solução: Type hints + suporte de IDE

É aqui que type hints realmente brilham. Embora Python não os imponha em runtime, sua IDE pode pegar erros antes mesmo de você rodar o código:

python
def add_numbers(a: int, b: int) -> int:
    """Add two numbers."""
    return a + b
 
# Sua IDE vai mostrar um aviso aqui (antes de você rodar o código)
result = add_numbers("Hello", "World")  # IDE: Warning - expected int, got str

Seu editor de código vê os type hints e pode avisar sobre incompatibilidades de tipo enquanto você digita, muito antes de rodar o código. Isso captura muitos bugs durante o desenvolvimento, em vez de em produção.

O desenvolvimento moderno em Python normalmente funciona assim:

  1. Você escreve código com type hints
  2. Sua IDE mostra avisos quando os tipos não batem
  3. Você corrige os problemas antes de rodar o código
  4. Erros em runtime por incompatibilidade de tipos ficam muito mais raros

O type hint não impede o erro em runtime, mas sua IDE o usa para impedir que você escreva código com bugs em primeiro lugar!

O melhor dos dois mundos

Type hints dão ao Python o melhor dos dois mundos — pegando a maioria dos erros cedo enquanto mantém flexibilidade:

Segurança no desenvolvimento: sua IDE e verificadores de tipo capturam a maioria dos erros de tipo durante o desenvolvimento, então você encontra bugs cedo.

python
def process(data: list) -> list:
    return [x * 2 for x in data]
 
# Se você acidentalmente passar uma string:
process("hello")  # IDE warns: expected list, got str
# Você corrige antes de rodar o código!

Flexibilidade em runtime: Python ainda roda código com tipos incompatíveis, o que pode ser útil para prototipagem rápida ou quando você intencionalmente quer aceitar múltiplos tipos.

python
def add_numbers(a: int, b: int) -> int:
    return a + b
 
# Python vai rodar isso, mesmo que os tipos não batam
print(add_numbers(5.5, 3.2))        # Output: 8.7 (works!)
print(add_numbers("Hi", " there"))  # Output: Hi there (also works!)

Essa flexibilidade significa que você não fica preso em um sistema de tipos rígido. Quando você precisa quebrar as regras (para testes, prototipagem ou casos de uso legítimos), Python permite. Mas quando você está escrevendo código de produção, sua IDE mantém você seguro.

Lembre-se da regra de ouro: type hints não mudam o comportamento em runtime do Python — eles só dão a você e às suas ferramentas a informação necessária para detectar problemas cedo. Você ainda precisa ter cuidado, mas agora tem aliados poderosos cuidando das suas costas.

42.3) Anotando funções: parâmetros e valores de retorno

O uso mais comum de type hints é anotar parâmetros e valores de retorno de funções. Isso diz aos leitores (e às ferramentas) quais tipos uma função espera e produz. Vamos começar com o caso mais simples e evoluir gradualmente.

O básico: anotações de parâmetros

Para adicionar um type hint a um parâmetro, coloque dois-pontos depois do nome do parâmetro, seguido do tipo:

python
def greet(name: str):
    """Greet a person by name."""
    return f"Hello, {name}!"
 
# Usage
message = greet("Alice")
print(message)  # Output: Hello, Alice!

A sintaxe name: str significa "o parâmetro name deveria ser uma string." Você pode adicionar type hints a múltiplos parâmetros:

python
def calculate_area(width: float, height: float):
    """Calculate the area of a rectangle."""
    return width * height
 
# Usage
area = calculate_area(5.0, 3.0)
print(area)  # Output: 15.0

Aqui, tanto width quanto height são anotados como float. A função funciona do mesmo jeito que antes — type hints não mudam o comportamento — mas agora sua IDE sabe quais tipos esperar.

Adicionando anotações de tipo de retorno

Para especificar qual tipo uma função retorna, adicione -> type depois da lista de parâmetros e antes dos dois-pontos:

python
def get_full_name(first: str, last: str) -> str:
    """Combine first and last names."""
    return f"{first} {last}"
 
# Usage
name = get_full_name("John", "Doe")
print(name)  # Output: John Doe

O -> str significa "esta função retorna uma string." Anotações de tipo de retorno são especialmente úteis quando o tipo de retorno não é óbvio pelo nome da função:

python
def is_adult(age: int) -> bool:
    """Check if someone is an adult (18 or older)."""
    return age >= 18
 
# Usage
adult = is_adult(25)
print(adult)  # Output: True

Sem olhar para a implementação, você já sabe imediatamente que essa função retorna um valor booleano.

Juntando tudo: uma função completa

A maioria das funções terá tanto anotações de tipo de parâmetro quanto de retorno. Veja como é uma função totalmente anotada:

python
def calculate_discount(price: float, discount_percent: float) -> float:
    """Calculate the discounted price."""
    discount_amount = price * (discount_percent / 100)
    return price - discount_amount
 
# Usage
original_price = 100.0
discount = 20.0
final_price = calculate_discount(original_price, discount)
print(f"Final price: ${final_price:.2f}")  # Output: Final price: $80.00

A assinatura dessa função diz tudo o que você precisa saber:

  • Ela recebe dois parâmetros float: price e discount_percent
  • Ela retorna um valor float
  • Você não precisa ler a implementação para entender como usar essa função

Vamos ver outro exemplo com tipos diferentes:

python
def repeat_message(message: str, times: int) -> str:
    """Repeat a message a specified number of times."""
    return message * times
 
# Usage
repeated = repeat_message("Hello! ", 3)
print(repeated)  # Output: Hello! Hello! Hello! 

Os type hints deixam claro que você passa uma string e um inteiro, e recebe de volta uma string.

Trabalhando com valores padrão

Quando um parâmetro tem um valor padrão, coloque o type hint entre o nome do parâmetro e o valor padrão:

python
def create_greeting(name: str, formal: bool = False) -> str:
    """Create a greeting message."""
    if formal:
        return f"Good day, {name}."
    return f"Hi, {name}!"
 
# Usage
print(create_greeting("Alice"))              # Output: Hi, Alice!
print(create_greeting("Bob", formal=True))   # Output: Good day, Bob.

A sintaxe formal: bool = False significa "formal é um booleano com valor padrão False."

Você pode ter múltiplos parâmetros com padrões, todos anotados:

python
def format_price(amount: float, currency: str = "USD", decimals: int = 2) -> str:
    """Format a price with currency symbol."""
    if currency == "USD":
        symbol = "$"
    elif currency == "EUR":
        symbol = "€"
    else:
        symbol = currency
    
    return f"{symbol}{amount:.{decimals}f}"
 
# Usage
print(format_price(99.99))                          # Output: $99.99
print(format_price(99.99, "EUR"))                   # Output: €99.99
print(format_price(99.995, "USD", 3))               # Output: $99.995

Cada parâmetro mostra claramente seu tipo e valor padrão, tornando a função fácil de entender e usar.

Caso especial: funções que não retornam valores

Algumas funções apenas executam ações (como imprimir ou escrever em um arquivo) sem retornar um valor. Para deixar claro que essas funções não retornam nada, use -> None:

python
def print_report(title: str, data: list) -> None:
    """Print a formatted report."""
    print(f"=== {title} ===")
    for item in data:
        print(f"  - {item}")
    # Sem statement return, então retorna None implicitamente
 
# Usage
print_report("Sales Data", [100, 150, 200])

Output:

=== Sales Data ===
  - 100
  - 150
  - 200

A anotação -> None indica explicitamente que essa função não retorna um valor significativo.

Por que usar -> None?

  • Clareza: deixa sua intenção explícita — esta função é para ações, não resultados
  • Suporte de IDE: sua IDE pode avisar se você acidentalmente tentar usar o valor de retorno

42.4) Anotações simples de variáveis

Embora type hints sejam mais comumente usados com funções, você também pode anotar variáveis. Vamos ver como isso funciona e quando isso é realmente útil.

Sintaxe básica de anotação de variáveis

Para anotar uma variável, use a mesma sintaxe de dois-pontos que com parâmetros de funções:

python
# Anotando variáveis
name: str = "Alice"
age: int = 30
height: float = 5.7
is_student: bool = True
 
print(f"{name} is {age} years old")  # Output: Alice is 30 years old

A sintaxe name: str = "Alice" significa "a variável name é uma string e tem o valor 'Alice'." A anotação não muda como a variável funciona — é puramente informativa.

Anotações de variáveis frequentemente são puladas

Na prática, anotações de variáveis raramente são usadas. O motivo é simples: Python consegue inferir o tipo a partir do valor, então anotações geralmente são redundantes:

python
# Estas anotações são desnecessárias
name: str = "Alice"  # Obviamente uma string
count: int = 0  # Obviamente um int
prices: list = [10.99, 5.50]  # Obviamente uma lista
settings: dict = {}  # Obviamente um dict
 
# Em vez disso, é só escrever assim
name = "Alice"
count = 0
prices = [10.99, 5.50]
settings = {}

Quando você escreve name = "Alice", tanto você quanto sua IDE sabem imediatamente que é uma string. A anotação não adiciona nenhuma informação útil.

Em código Python do mundo real, você raramente vai ver anotações de variáveis. Isso é normal e esperado. Anotações de funções são bem mais importantes e comuns.

O caso útil: declarar variáveis antes de atribuir

Existe uma situação em que anotações de variáveis são genuinamente úteis: quando você precisa declarar uma variável antes de atribuir um valor a ela.

python
def calculate_statistics(numbers: list) -> dict:
    """Calculate basic statistics from a list of numbers."""
    # Declare variáveis antes de usá-las
    total: float
    count: int
    average: float
    
    # Agora atribua valores
    total = sum(numbers)
    count = len(numbers)
    average = total / count if count > 0 else 0.0
    
    return {
        "total": total,
        "count": count,
        "average": average
    }
 
# Usage
result = calculate_statistics([10, 20, 30, 40])
print(f"Average: {result['average']}")  # Output: Average: 25.0

Sem anotações, você não consegue declarar uma variável sem também atribuir um valor a ela. As anotações permitem que você especifique os tipos desde o começo, o que pode deixar a estrutura do código mais clara.

Este é o principal caso de uso prático para anotações de variáveis.

Lembre-se: variáveis podem ser reatribuídas a tipos diferentes

Mesmo com uma anotação de tipo, você pode reatribuir uma variável para um tipo diferente:

python
# Comece com uma string
value: str = "hello"
print(value)  # Output: hello
 
# Reatribua para um tipo diferente - Python permite isso
value = 42
print(value)  # Output: 42
 
# Outra mudança de tipo - ainda permitido
value = [1, 2, 3]
print(value)  # Output: [1, 2, 3]

Sua IDE ou um verificador de tipo estático vai avisar sobre essas mudanças de tipo, mas o próprio Python não as impede. Type hints guiam você em direção à consistência, mas não a impõem em runtime.

42.5) Lidando com "None": tipos opcionais e o operador |

Um dos padrões mais comuns em Python é uma função que pode retornar um valor ou pode retornar None. Por exemplo, buscar um item pode dar certo (retornando o item) ou falhar (retornando None). Type hints fornecem formas claras de expressar esse padrão.

O problema: funções que podem retornar None

Considere esta função que busca um usuário:

python
def find_user_by_email(email: str) -> dict:
    """Find a user by email address."""
    users = [
        {"name": "Alice", "email": "alice@example.com"},
        {"name": "Bob", "email": "bob@example.com"}
    ]
    
    for user in users:
        if user["email"] == email:
            return user
    
    return None  # Incompatibilidade de tipo! Isso contradiz o hint -> dict
 
# Usage
user = find_user_by_email("alice@example.com")
if user:
    print(f"Found: {user['name']}")  # Output: Found: Alice
else:
    print("User not found")

O type hint -> dict é enganoso porque a função pode retornar None. Um verificador de tipo estático avisaria você que retornar None não corresponde ao tipo de retorno declarado dict.

Solução: usando o operador | para tipos opcionais

Python 3.10 introduziu o operador | para type hints, que significa "ou". Você pode usá-lo para indicar que uma função pode retornar um tipo ou outro:

python
def find_user_by_email(email: str) -> dict | None:
    """Find a user by email address. Returns None if not found."""
    users = [
        {"name": "Alice", "email": "alice@example.com"},
        {"name": "Bob", "email": "bob@example.com"}
    ]
    
    for user in users:
        if user["email"] == email:
            return user
    
    return None
 
# Usage
user = find_user_by_email("alice@example.com")
if user:
    print(f"Found: {user['name']}")  # Output: Found: Alice
 
missing = find_user_by_email("charlie@example.com")
if missing is None:
    print("User not found")  # Output: User not found

O type hint -> dict | None significa "esta função retorna ou um dicionário ou None." Isso descreve com precisão o comportamento da função.

Observação: em código Python mais antigo (antes do 3.10), você pode ver Optional[dict] do módulo typing em vez de dict | None. Eles significam a mesma coisa, mas | é a sintaxe moderna e preferida.

Usando | com múltiplos tipos

Você pode usar | para indicar mais de dois tipos possíveis:

python
def parse_value(text: str) -> int | float | None:
    """Parse a string into a number. Returns None if parsing fails."""
    try:
        # Tente fazer o parse como inteiro primeiro
        if '.' not in text:
            return int(text)
        # Caso contrário, faça o parse como float
        return float(text)
    except ValueError:
        return None
 
# Usage
print(parse_value("42"))      # Output: 42 (int)
print(parse_value("3.14"))    # Output: 3.14 (float)
print(parse_value("invalid")) # Output: None

O type hint -> int | float | None significa que a função pode retornar um inteiro, um float, ou None.

Verificando None: boas práticas

Quando uma função pode retornar None, sempre verifique None antes de usar o resultado. Caso contrário, você corre o risco de erros ao tentar usar None como se fosse o tipo esperado:

python
def get_user_age(user_id: int) -> int | None:
    """Get user's age. Returns None if user not found."""
    users = {1: 25, 2: 30, 3: 35}
    return users.get(user_id)
 
# Sempre verifique None antes de usar o valor
age = get_user_age(1)
if age is not None:
    print(f"User is {age} years old")  # Output: User is 25 years old
    if age >= 18:
        print("User is an adult")  # Output: User is an adult
else:
    print("User not found")
 
# Para usuários inexistentes
age = get_user_age(999)
if age is None:
    print("User not found")  # Output: User not found

A chave é usar if age is not None: ou if age is None: para verificar explicitamente antes de usar o valor.

Parâmetros opcionais com | None

Você também pode usar | com parâmetros, muitas vezes combinado com valores padrão:

python
def format_name(first: str, middle: str | None = None, last: str = "") -> str:
    """Format a full name. Middle name is optional."""
    if middle and last:
        return f"{first} {middle} {last}"
    elif last:
        return f"{first} {last}"
    return first
 
# Usage
print(format_name("John", "Q", "Doe"))    # Output: John Q Doe
print(format_name("Jane", None, "Smith")) # Output: Jane Smith
print(format_name("Prince"))              # Output: Prince

O type hint middle: str | None = None indica que middle pode ser uma string ou None, com None como valor padrão. Esse é um padrão comum para parâmetros opcionais.

42.6) Lendo type hints comuns: list, dict, tuple

Conforme você lê código Python escrito por outras pessoas, você vai encontrar type hints para coleções como listas, dicionários e tuplas. O Python moderno fornece formas claras de especificar não só que algo é uma lista, mas que tipo de itens a lista contém.

Observação: a sintaxe mostrada aqui (list[int], dict[str, int], etc.) funciona no Python 3.9+. Em código mais antigo, você pode ver List[int] e Dict[str, int] (com letra maiúscula) do módulo typing — eles funcionam do mesmo jeito.

Type hints básicos de coleções

Os type hints de coleções mais simples só especificam o tipo da coleção:

python
def print_items(items: list) -> None:
    """Print all items in a list."""
    for item in items:
        print(item)
 
def get_user_settings() -> dict:
    """Get user settings as a dictionary."""
    return {"theme": "dark", "notifications": True}
 
def get_position() -> tuple:
    """Get x, y position."""
    return (10, 20)

Esses hints dizem o tipo da coleção, mas não o que tem dentro.

Listas: especificando tipos dos itens

Para especificar que tipo de itens uma lista contém, use colchetes:

python
def calculate_total(prices: list[float]) -> float:
    """Calculate the total of all prices."""
    return sum(prices)
 
# Usage
total = calculate_total([10.99, 5.50, 3.25])
print(f"Total: ${total:.2f}")  # Output: Total: $19.74

O type hint list[float] significa "uma lista contendo floats." Isso é mais informativo do que apenas list.

Aqui vai outro exemplo com strings:

python
def format_names(names: list[str]) -> str:
    """Format a list of names as a comma-separated string."""
    return ", ".join(names)
 
# Usage
students = ["Alice", "Bob", "Charlie"]
print(format_names(students))  # Output: Alice, Bob, Charlie

O type hint list[str] significa "uma lista contendo strings."

Dicionários: especificando tipos de chaves e valores

Para dicionários, especifique tanto o tipo da chave quanto o tipo do valor:

python
def get_student_grades() -> dict[str, int]:
    """Get student names mapped to their grades."""
    return {
        "Alice": 95,
        "Bob": 87,
        "Charlie": 92
    }
 
# Usage
grades = get_student_grades()
for name, grade in grades.items():
    print(f"{name}: {grade}")

Output:

Alice: 95
Bob: 87
Charlie: 92

O type hint dict[str, int] significa "um dicionário com chaves string e valores inteiros."

Aqui vai um exemplo em que valores podem ser múltiplos tipos:

python
def get_user_data(user_id: int) -> dict[str, str | int]:
    """Get user data. Values can be strings or integers."""
    return {
        "name": "Alice",
        "email": "alice@example.com",
        "age": 30,
        "id": 12345
    }
 
# Usage
user = get_user_data(1)
print(f"{user['name']} is {user['age']} years old")  # Output: Alice is 30 years old

O type hint dict[str, str | int] significa "um dicionário com chaves string e valores que são ou strings ou inteiros."

Tuplas: comprimento fixo e variável

Tuplas são diferentes de listas porque elas frequentemente têm uma estrutura fixa. Você pode especificar o tipo de cada posição:

python
def get_user_info(user_id: int) -> tuple[str, int, bool]:
    """
    Get user information as a tuple.
    Returns: (name, age, is_active)
    """
    return ("Alice", 30, True)
 
# Usage
name, age, active = get_user_info(1)
print(f"{name}, {age}, active: {active}")  # Output: Alice, 30, active: True

O type hint tuple[str, int, bool] significa "uma tupla com exatamente três elementos: uma string, um inteiro e um booleano, nessa ordem."

Para tuplas de comprimento variável com itens do mesmo tipo, use reticências (...):

python
# Tupla de comprimento fixo: exatamente 2 floats
def get_2d_point() -> tuple[float, float]:
    """Get 2D coordinates (x, y)."""
    return (10.5, 20.3)
 
# Tupla de comprimento variável: qualquer quantidade de floats
def get_coordinates() -> tuple[float, ...]:
    """Get coordinates. Can be 2D, 3D, or any dimension."""
    return (10.5, 20.3, 15.7)  # 3D in this case
 
# Usage
point = get_2d_point()
coords = get_coordinates()
print(f"2D point: {point}")       # Output: 2D point: (10.5, 20.3)
print(f"Coordinates: {coords}")   # Output: Coordinates: (10.5, 20.3, 15.7)

O type hint tuple[float, ...] significa "uma tupla contendo qualquer quantidade de floats." O ... significa "qualquer quantidade deste tipo."

Coleções aninhadas

Você pode aninhar type hints para estruturas de dados complexas. Vamos começar com um exemplo simples:

python
def get_scores_by_student() -> dict[str, list[int]]:
    """Get test scores for each student."""
    return {
        "Alice": [95, 87, 92],
        "Bob": [88, 91, 85],
        "Charlie": [90, 88, 94]
    }
 
# Usage
scores = get_scores_by_student()
for name, tests in scores.items():
    average = sum(tests) / len(tests)
    print(f"{name}: {average:.1f}")

Output:

Alice: 91.3
Bob: 88.0
Charlie: 90.7

O type hint dict[str, list[int]] significa "um dicionário com chaves string e valores do tipo lista de inteiros."

Aqui vai um exemplo mais complexo:

python
def get_student_records() -> list[dict[str, str | int]]:
    """Get a list of student records."""
    return [
        {"name": "Alice", "age": 20, "major": "CS"},
        {"name": "Bob", "age": 21, "major": "Math"},
        {"name": "Charlie", "age": 19, "major": "Physics"}
    ]
 
# Usage
students = get_student_records()
for student in students:
    print(f"{student['name']}, {student['age']}, {student['major']}")

Output:

Alice, 20, CS
Bob, 21, Math
Charlie, 19, Physics

O type hint list[dict[str, str | int]] significa "uma lista de dicionários, em que cada dicionário tem chaves string e valores que são ou strings ou inteiros."

Lendo type hints: uma referência rápida

Quando você encontrar type hints no código, veja como lê-los:

Coleções:

  • list[int] - "uma lista de inteiros"
  • dict[str, float] - "um dicionário com chaves string e valores float"
  • tuple[str, int] - "uma tupla com exatamente dois itens: uma string, depois um inteiro"
  • tuple[float, ...] - "uma tupla contendo qualquer quantidade de floats"

Opcional e múltiplos tipos:

  • int | None - "um inteiro ou None"
  • str | int | float - "uma string, um inteiro, ou um float"

Aninhados:

  • list[dict[str, int]] - "uma lista de dicionários (cada dict tem chaves string e valores inteiros)"
  • dict[str, list[float]] - "um dicionário com chaves string e valores do tipo lista de floats"

Observação: em código mais antigo (Python < 3.10), você pode ver Union[int, str] em vez de int | str, ou Optional[int] em vez de int | None. Eles significam a mesma coisa.

© 2025. Primesoft Co., Ltd.
support@primesoft.ai