Dataclasses em Python

PythonBeginner
Pratique Agora

Introdução

Neste laboratório, você será apresentado às Dataclasses do Python (Python Dataclasses), um recurso poderoso disponível a partir do Python 3.7. Dataclasses são classes Python comuns usadas principalmente para armazenar dados. Elas utilizam um decorador, @dataclass, para gerar automaticamente métodos especiais como __init__() e __repr__(), o que reduz significativamente o código repetitivo (boilerplate code).

Ao longo deste laboratório, você aprenderá a:

  • Definir uma dataclass simples.
  • Compará-la com uma implementação de classe padrão.
  • Definir valores padrão para os campos (fields).
  • Lidar com dicas de tipo (type hints), incluindo o uso de typing.Any.

Ao final deste laboratório, você será capaz de escrever classes mais limpas e concisas focadas em dados em Python.

Funcionalidades da Dataclass

Nesta etapa, você aprenderá as funcionalidades básicas das dataclasses. Começaremos analisando uma classe Python padrão para armazenar dados e, em seguida, veremos como uma dataclass a simplifica.

Uma classe padrão exige que você escreva manualmente o método __init__ para inicializar os atributos.

Por exemplo, uma classe para representar um número seria assim:

class Number:
    def __init__(self, val):
        self.val = val

obj = Number(2)
print(obj.val)

Com o decorator @dataclass, o Python gera automaticamente o método __init__, bem como outros métodos úteis como __repr__ para uma representação em string limpa. O requisito principal é que você deve fornecer type hints (dicas de tipo) para os campos.

Nota: Se você não está familiarizado com o uso do WebIDE (semelhante ao VS Code), aprenda primeiro neste laboratório neste laboratório.

Vamos criar a mesma classe Number usando o decorator @dataclass.

Abra o arquivo features.py no explorador de arquivos à esquerda. Adicione o seguinte código a ele:

from dataclasses import dataclass

@dataclass  ## Este decorator converte a classe em uma dataclass
class Number:
    val: int  ## Type hint é obrigatório

## O método __init__ é criado automaticamente
obj = Number(2)

## Imprime o valor do atributo 'val'
print(obj.val)

## O método __repr__ também é criado automaticamente, fornecendo uma representação agradável
print(obj)

Agora, execute o script no terminal para ver a saída.

python3 features.py

Você verá a seguinte saída. A primeira linha é o valor do atributo, e a segunda linha é a representação limpa do objeto fornecida pelo método __repr__ gerado automaticamente.

2
Number(val=2)

Como você pode ver, a versão com dataclass é mais concisa e requer menos código repetitivo (boilerplate code).

Usando Valores Padrão

Nesta etapa, você explorará como adicionar valores padrão aos campos (fields) de uma dataclass. Este é um requisito comum quando alguns atributos são opcionais.

Uma regra fundamental a ser lembrada é que qualquer campo com um valor padrão deve ser declarado após todos os campos sem valores padrão.

Vamos criar uma dataclass Product onde name é um campo obrigatório, mas count e price são opcionais e possuem valores padrão.

Abra o arquivo defaults.py no explorador de arquivos. Adicione o seguinte código:

from dataclasses import dataclass

@dataclass
class Product:
    name: str        ## Campo obrigatório
    count: int = 0   ## Campo opcional com valor padrão
    price: float = 0.0  ## Campo opcional com valor padrão

## Cria uma instância fornecendo apenas o campo obrigatório 'name'
obj = Product("Python")

## Imprime os atributos para ver os valores padrão em ação
print(f"Name: {obj.name}")
print(f"Count: {obj.count}")
print(f"Price: {obj.price}")

Agora, execute o script no terminal.

python3 defaults.py

A saída mostrará que count e price receberam seus valores padrão automaticamente.

Name: Python
Count: 0
Price: 0.0

Esta funcionalidade torna a criação de objetos com atributos opcionais muito conveniente.

Manipulação de Type Hints

Nesta etapa, você entenderá a exigência de usar dicas de tipo (type hints) em dataclasses. Como você viu, fornecer um tipo para cada campo é obrigatório ao definir uma dataclass.

No entanto, pode haver situações em que você não deseja ou não pode especificar um tipo de dado concreto para um campo. Para esses casos, o módulo typing do Python fornece um tipo especial chamado Any. Usar Any informa ao verificador de tipos (type checker) que um valor pode ser de qualquer tipo.

Vamos criar uma dataclass onde usamos Any para os campos.

Abra o arquivo type_hints.py no explorador de arquivos e adicione o seguinte código:

from dataclasses import dataclass
from typing import Any

@dataclass
class WithoutExplicitTypes:
   name: Any
   value: Any = 42

## Cria uma instância onde 'name' é uma string. 'value' usa seu padrão.
obj1 = WithoutExplicitTypes(name="Test")
print(f"Object 1: name={obj1.name}, value={obj1.value}")

## Cria outra instância onde 'name' é um inteiro e 'value' é uma string.
obj2 = WithoutExplicitTypes(name=123, value="hello")
print(f"Object 2: name={obj2.name}, value={obj2.value}")

Agora, execute o script no terminal.

python3 type_hints.py

Você verá a seguinte saída, demonstrando que os campos podem conter valores de tipos diferentes.

Object 1: name=Test, value=42
Object 2: name=123, value=hello

Embora Any ofereça flexibilidade, geralmente é uma boa prática usar tipos específicos sempre que possível para tornar seu código mais robusto e autoexplicativo.

Resumo

Neste laboratório, você aprendeu a usar as dataclasses do Python para criar classes centradas em dados que são concisas e legíveis.

Você abordou os seguintes conceitos-chave:

  • O decorador @dataclass gera automaticamente métodos especiais como __init__() e __repr__(), reduzindo o código repetitivo (boilerplate code).
  • Como definir campos com valores padrão e a regra de que eles devem seguir os campos sem valores padrão.
  • A natureza obrigatória das dicas de tipo (type hints) em dataclasses e como usar typing.Any para campos onde um tipo específico não é desejado.

As dataclasses são uma ferramenta poderosa no Python moderno para escrever código limpo, de fácil manutenção e eficiente, especialmente ao lidar com classes que armazenam primariamente estado (state).