Desafio Python

Ordenação com dataclass e campo calculado

Enunciado

Crie uma dataclass Retangulo com campos largura (float) e altura (float). A classe deve ser ordenável (order=True) e ter um campo calculado area que não é passado no construtor, mas calculado após a inicialização (use __post_init__). O campo area não deve participar da ordenação nem da igualdade. Crie uma lista de retângulos, ordene-a e imprima. Além disso, mostre que dois retângulos com mesma largura e altura são iguais, independentemente da área (que será igual de qualquer forma).

Requisitos

  • Usar @dataclass(order=True)
  • Definir campo area com field(init=False, compare=False) e calculá-lo em post_init
  • Ordenar uma lista de retângulos e imprimir
  • Demonstrar igualdade entre retângulos com mesmas dimensões

Código inicial

from dataclasses import dataclass, field

# Defina a dataclass Retangulo aqui

# Crie lista, ordene e imprima

# Compare dois retângulos iguais

Saída esperada

Retangulo(largura=2.0, altura=3.0, area=6.0)
Retangulo(largura=1.0, altura=5.0, area=5.0)
Retangulo(largura=3.0, altura=1.0, area=3.0)
Iguais? True
Ver dica

O campo area deve ser declarado com field(init=False, compare=False) e atribuído em post_init.

Mostrar solução
from dataclasses import dataclass, field

@dataclass(order=True)
class Retangulo:
    largura: float
    altura: float
    area: float = field(init=False, compare=False)

    def __post_init__(self):
        self.area = self.largura * self.altura

# Criando retângulos
r1 = Retangulo(2.0, 3.0)
r2 = Retangulo(1.0, 5.0)
r3 = Retangulo(3.0, 1.0)

# Ordenando (usa largura e depois altura)
retangulos = [r1, r2, r3]
for r in sorted(retangulos):
    print(r)

# Igualdade ignora area (mas area é igual para mesmas dimensões)
r4 = Retangulo(2.0, 3.0)
print(f'Iguais? {r1 == r4}')

Passo a passo

  1. Importamos dataclass e field.
  2. Decoramos Retangulo com @dataclass(order=True): isso gera métodos de comparação como __lt__, __le__, etc., baseados nos campos na ordem de declaração.
  3. Declaramos largura e altura como campos normais.
  4. Declaramos area com field(init=False, compare=False): init=False significa que não aparece no __init__; compare=False exclui da ordenação e igualdade.
  5. Definimos __post_init__, que é chamado após o __init__ automático, para calcular area.
  6. Criamos três retângulos.
  7. Ordenamos a lista com sorted(), que usará __lt__ gerado, comparando primeiro largura, depois altura.
  8. Imprimimos cada retângulo; o __repr__ inclui area porque é um campo.
  9. Criamos r4 igual a r1 e comparamos: como area não participa da comparação, são iguais.

Por que funciona

order=True instrui a dataclass a gerar métodos de ordenação baseados nos campos que participam da comparação (todos, exceto os com compare=False). O campo area é calculado dinamicamente em __post_init__, garantindo que esteja sempre correto. init=False evita que o usuário precise passá-lo.

Erros comuns

  • Esquecer compare=False em area: isso faria com que a ordenação considerasse area, o que pode não ser desejado.
  • Não usar field(init=False): o campo apareceria no construtor, exigindo um valor.
  • Tentar modificar area após a criação: se a classe não for frozen, é possível, mas quebra a consistência. Se for frozen, __post_init__ não pode atribuir (a menos que use object.__setattr__).

Outra forma de resolver

Poderia usar @property para area em vez de campo, mas aí não apareceria no __repr__ e não seria um campo de dataclass.

Saída esperada

Retangulo(largura=2.0, altura=3.0, area=6.0)
Retangulo(largura=1.0, altura=5.0, area=5.0)
Retangulo(largura=3.0, altura=1.0, area=3.0)
Iguais? True