Médio Python

Dataclass com field e frozen

Enunciado

Crie uma dataclass Pessoa com os campos nome (str), idade (int) e tags (list[str]) que deve ter valor padrão vazio e não participar da comparação de igualdade. Torne a classe imutável (frozen). Instancie duas pessoas com as mesmas informações, mas tags diferentes, e mostre que são iguais. Tente modificar um atributo e mostre que gera erro.

Requisitos

  • Usar @dataclass com frozen=True
  • Usar field para o campo tags com default_factory=list e compare=False
  • Demonstrar igualdade ignorando tags
  • Demonstrar que atributos não podem ser modificados

Código inicial

from dataclasses import dataclass, field

# Defina a dataclass Pessoa aqui

# Crie duas pessoas com tags diferentes e compare

# Tente modificar um atributo e capture o erro

Saída esperada

p1 == p2? True
p1.tags = ['nova'] -> erro: cannot assign to field 'tags'
Ver dica

Para capturar o erro de atribuição, use try/except FrozenInstanceError.

Mostrar solução
from dataclasses import dataclass, field

@dataclass(frozen=True)
class Pessoa:
    nome: str
    idade: int
    tags: list[str] = field(default_factory=list, compare=False)

p1 = Pessoa('Ana', 30, ['amiga'])
p2 = Pessoa('Ana', 30, ['colega'])
print(f'p1 == p2? {p1 == p2}')

try:
    p1.tags = ['nova']
except Exception as e:
    print(f'p1.tags = [\'nova\'] -> erro: {e}')

Passo a passo

  1. Importamos dataclass e field.
  2. Decoramos Pessoa com @dataclass(frozen=True) para torná-la imutável.
  3. Definimos os campos nome, idade e tags.
  4. Para tags, usamos field(default_factory=list, compare=False): default_factory cria uma lista nova a cada instância e compare=False exclui o campo da comparação de igualdade.
  5. Criamos duas pessoas com mesmos nome e idade, mas tags diferentes.
  6. Comparamos: como tags não entra na comparação, são iguais.
  7. Tentamos atribuir a p1.tags; como a classe é frozen, isso levanta FrozenInstanceError.
  8. Capturamos a exceção e imprimimos a mensagem.

Por que funciona

frozen=True impede a modificação de atributos após a criação, simulando imutabilidade. field permite personalizar o comportamento de cada campo, como valor padrão e participação na comparação. Juntos, eles permitem criar objetos seguros e com semântica clara.

Erros comuns

  • Esquecer default_factory e usar tags: list = []: isso causa ValueError: mutable default <class 'list'> for field tags is not allowed.
  • Tentar modificar um atributo de instância frozen sem capturar a exceção: o programa quebra.
  • Achar que compare=False afeta a impressão: ele só afeta __eq__ e ordenação.

Outra forma de resolver

Poderia usar @dataclass(frozen=True) sem field e definir tags como tuple, que é imutável. Mas aí perderia a flexibilidade de lista.

Saída esperada

p1 == p2? True
p1.tags = ['nova'] -> erro: cannot assign to field 'tags'