DEV Community

Cover image for Documentando código Python com Type Hints
Flávio Filipe
Flávio Filipe

Posted on • Edited on

2 2

Documentando código Python com Type Hints

Introdução

Fiquei um pouco confuso ao começar a estudar sobre a declaração explícita com Python. Ao tentar comparar com outras linguagens como Java, C++ ou Typescript a compreensão começa a ficar um pouco problemática. Para entender o que é, para que serve e quando utilizar é preciso entender o motivo da sua criação.

Para começar precisamos ressaltar duas coisas importantes sobre o Python:

  1. É uma linguagem fortemente tipada, isto é, cada variável tem um tipo e em alguns casos é preciso convertê-las para realizar operações, por exemplo: ‘1’ + 1 irá retornar uma exceção pois os tipos são diferentes.

  2. É uma linguagem de tipagem dinâmica. Isso significa que podemos alterar o tipo da variável ao longo do programa, como por exemplo:

n = 2 # n é do tipo inteiro
n = teste # n é do tipo string
Enter fullscreen mode Exit fullscreen mode

Tipo de dados

Para conhecermos melhor os tipos em python podemos utilizar o método type(var).

Os mais comuns são:

Tipo Descrição Exemplo
str Texto 'hello'
int Número Inteiro 1 , 2, 3
float Número Real 1.0, 2.0
list Lista [1,2,3]
dict Dicionário {'title': 'post 1', 'category': 1 }

Vantagens da Tipagem Estática

A grande vantagem de se trabalhar com linguagens de tipagem estática é que ao definir o seu tipo: garantimos que não teremos problemas em tentar somar um inteiro com uma string durante o desenvolvimento do código.
Outra vantagem é poder entender os tipos de dados que uma função está esperando, apontando um erro imediato na IDE durante a escrita do código caso o parâmetro passado seja de um tipo diferente.

Mas e se tratando de Python, como podemos ganhar agilidade no desenvolvimento e evitar erros comuns com os tipos de dados e deixar claro para quem for dar manutenção no futuro os tipos de dados que esperamos trabalhar?

Vamos considerar o código abaixo:

def somar(a, b):
    return a+b
Enter fullscreen mode Exit fullscreen mode

Olhando para ele fica difícil saber o real propósito da sua criação. Ele foi criado para somar números, textos ou listas? O que aconteceria se eu passasse um número no primeiro parâmetro e um texto no segundo? Precisamos deixar claro para quem precisar olhar para este código no futuro.

Docstring

Na PEP-257, com a chegada do Docstring, é apresentada uma convenção para documentar nossas funções. Abaixo um exemplo com a tentativa de facilitar nosso entendimento:

def somar(a, b):
    '''
        Retorna a soma de dois números inteiros
        Params:
            a (int)
            b (int)
    '''
    return a+b
Enter fullscreen mode Exit fullscreen mode

Dependendo da IDE ou editor de texto utilizado, ao chamar esta função e passar o mouse por cima, ele irá apresentar a sua documentação. Mas pensando no cenário onde teremos vários métodos e para todos eles precisaríamos criar um docstring somente para informar o tipo de parâmetro e seu retorno acaba se tornando uma tarefa repetitiva.

Decoradores para Funções e Métodos

Na PEP-318, com a chegada dos decoradores para as funções e métodos, é apresentado uma sintaxe para ser utilizada com a finalidade de definir os tipos de parâmetros e retorno, assim poderíamos simplificar nossa documentação criando uma funçao de para informar os parâmetros e retorno:

@accepts (int, int)
@returns (int)
def somar(a, b):
    return a+b
Enter fullscreen mode Exit fullscreen mode

Aqui temos alguns exemplos da utilização dos decorators para facilitar o entendimento deles:

Finalmente os Annotations

No ano de 2006, com a chegada do Python 3, a PEP-3107 nos trouxe uma outra forma de documentar nosso código com as Anotações de Função. Agora nós podemos fazer as anotações dentro do parâmetro da função e logo em seguida informar seu retorno, que basicamente fazem o uso das anotations para fazer o mapeamento.

Type Hints

A PEP-483 nos trás uma nova “teoria” da proposta de implementação de tipos para o Python 3.5 Segue a citação que deixa bem claro o real propósito da nova implementação sugerida:

É importante que o usuário seja capaz de definir os tipos de uma forma que possa ser entendida por verificadores de tipo. O objetivo deste PEP é propor uma forma sistemática de definir tipos para anotações de tipo de variáveis ​​e funções usando a sintaxe PEP 3107 . Essas anotações podem ser usadas para evitar muitos tipos de bugs, para fins de documentação, ou talvez até mesmo para aumentar a velocidade de execução do programa. Aqui, nos concentramos apenas em evitar bugs usando um verificador de tipo estático.

Porém é com a PEP-484 que isso acontece, com a chegada dos Type Hints ou Dicas de Tipo.

Segue um exemplo atualizado usando a nova sintaxe e falaremos sobre ele em seguida:

def somar(a: int, b: int) -> int:
    return a+b
Enter fullscreen mode Exit fullscreen mode

Agora, de uma forma mais semântica, conseguimos documentar melhor nosso método. Fica claro os tipos de dados de entrada e saída. Agora vamos à "confusão" citada no início deste artigo quando tentei comparar com outras linguagens.

Ao olharmos o histórico da implementação podemos perceber que esta funcionalidade está mais ligada a documentação e legibilidade do código ao invés de tornar a linguagem estática. Sendo assim, diferente de outras linguagens estáticas, ao fazer uma declaração explícita dos valores de entrada ele não nos obriga a informá-los ou proíbe a mudança de tipo ao longo do código. Isso significa que o código abaixo ainda é um código válido:

def somar(a: int, b: int) -> int:
    return a+b  

print(somar('a', 'b')) 

>> 'ab'
Enter fullscreen mode Exit fullscreen mode

Isso ocorre porque o intuito é DOCUMENTAR e não obrigar os tipos de entrada e saída. A grande vantagem desta funcionalidade está no desenvolvimento se combinado com a ferramenta de terceiros para fazer a validação no código.

Utilizando uma IDE ou um editor de texto configurado para o python, ao escrever o código acima seria apresentado um erro informando que os tipos de dados são incompatíveis. Outras ferramentas, como o MyPy, podem garantir se o código segue as regras de implementação propostas. Com isso em mente, vamos explorar um pouco mais o que podemos fazer com os Types Hints.

Eu estou utilizando o PyCharm, uma IDE feita para Python. Ela tem uma ótima integração com os Type Hints fazendo as validações em tempo de desenvolvimento. Mas sintam-se a vontade para testar em outros editores. Você poderá executar o MyPy sempre que quiser testar suas implementações

EXEMPLOS COM TYPE HINTS

Exemplo 1: Precisamos passar uma lista de palabras para um método fazer algum tipo de validação nelas. Assim, precisamos especificar o tipo de lista que estamos esperando:

def validar_palavras(palavras: list[str]) -> bool:
    return True if 'teste' in palavras else False

print(validate_words(['Nome', 'teste'])
Enter fullscreen mode Exit fullscreen mode

Utilizando o list[str] informamos que estamos esperando uma lista de palavras para nossa função. No método acima o -> bool informa que o retorno é um boleano (Verdadeiro ou Falso).

Exemplo 2: Precisamos dar as boas vindas ao suário. Por isso iremos criar um método que recebe o nome e o status do usuário, se ele tiver o status ATIVO irá retornar a mensagem: Bem vindo <NOME>!. Mas se ele possuir o status SUSPENSO deverá retornar a mensagem: Usuário Suspenso!

from typing import TypedDict  

from enum import Enum  


class UserStatus(Enum):  
    ATIVO = 1  
    SUSPENSO = 2  


class UserType(TypedDict):  
    nome: str  
    status: UserStatus  


def boas_vindas(usuario: UserType) -> str:  
    if usuario['status'] == UserStatus.SUSPENSO:  
        return 'Usuário Suspenso!'  

    return f"Bem vindo {usuario['nome']}"  


usuario_ativo: UserType = {'nome': 'Usuário Ativo', 'status': 'ativo'}  
usuario_suspenso: UserType = {'nome': 'Usuário Suspenso', 'status': UserStatus.SUSPENSO}  
print(boas_vindas(usuario_ativo))  
print(boas_vindas(usuario_suspenso))
Enter fullscreen mode Exit fullscreen mode

Para este exemplo nós criamos duas classes para ser a referência de tipo de entrada para nossa função. A class UserStatus será usada para definir nossos tipos padrões de status aceitos, enquanto a UserType serão os tipos de dados esperados pelo usuário.

Caso vc esteja utilizando o PyCharm ou algum editor configurado para reconhecer os tipos verá que poderá utilizar o autocomplete e caso passe algum parâmetro errado o próprio editor já acusa que há um erro:
Mensagem de tipo inesperado para o status do usuário

Você poderá usar também a opção do autocomplete apertando ctrl+espaço:
Sugestão de autocomplete para o usuário

Para conhecer mais como definir os tipos com classes poderá consultar a PEP-589.

Conclusão

Com a declaração explicita podemos:

  • Documentar melhor nosso código
  • Ter segurança na hora de fazer o refatoramento
  • Aproveitar ao máximo o autocomplete
  • Pensar melhor nos dados da nossa aplicação
  • Deixar o código mais claro e limpo

AWS Security LIVE! Stream

Go beyond the firewall

Security starts with people. Discover solutions to real-world challenges from AWS and AWS Partners on AWS Security LIVE!

Learn More

Top comments (0)

Image of PulumiUP 2025

Explore What’s Next in DevOps, IaC, and Security

Join us for demos, and learn trends, best practices, and lessons learned in Platform Engineering & DevOps, Cloud and IaC, and Security.

Save Your Spot

👋 Kindness is contagious

Dive into this informative piece, backed by our vibrant DEV Community

Whether you’re a novice or a pro, your perspective enriches our collective insight.

A simple “thank you” can lift someone’s spirits—share your gratitude in the comments!

On DEV, the power of shared knowledge paves a smoother path and tightens our community ties. Found value here? A quick thanks to the author makes a big impact.

Okay