Médio Python

Retângulo com área calculada

Enunciado

Crie uma classe Retangulo que armazena largura e altura como properties com validação (devem ser maiores que zero). Adicione uma property somente leitura area que calcula a área. Adicione também uma property perimetro que calcula o perímetro. Não armazene area nem perimetro como atributos; calcule-os dinamicamente.

Requisitos

  • As properties largura e altura devem ter getter e setter com validação (valor > 0).
  • A property area deve ser somente leitura e retornar largura * altura.
  • A property perimetro deve ser somente leitura e retornar 2 * (largura + altura).

Código inicial

class Retangulo:
    def __init__(self, largura, altura):
        pass

    @property
    def largura(self):
        pass

    @largura.setter
    def largura(self, valor):
        pass

    @property
    def altura(self):
        pass

    @altura.setter
    def altura(self, valor):
        pass

    @property
    def area(self):
        pass

    @property
    def perimetro(self):
        pass

Saída esperada

Area: 6
Perimetro: 10
Nova area: 12
Erro: Largura deve ser positiva
Ver dica

Para a área e perímetro, apenas retorne o cálculo usando self.largura e self.altura. Lembre-se de que não precisa de setter para elas.

Mostrar solução
class Retangulo:
    def __init__(self, largura, altura):
        self.largura = largura  # usa setter
        self.altura = altura    # usa setter

    @property
    def largura(self):
        return self._largura

    @largura.setter
    def largura(self, valor):
        if valor <= 0:
            raise ValueError('Largura deve ser positiva')
        self._largura = valor

    @property
    def altura(self):
        return self._altura

    @altura.setter
    def altura(self, valor):
        if valor <= 0:
            raise ValueError('Altura deve ser positiva')
        self._altura = valor

    @property
    def area(self):
        return self._largura * self._altura

    @property
    def perimetro(self):
        return 2 * (self._largura + self._altura)

# Testes
r = Retangulo(2, 3)
print(f'Area: {r.area}')
print(f'Perimetro: {r.perimetro}')
r.largura = 4
print(f'Nova area: {r.area}')
try:
    r.largura = -1
except ValueError as e:
    print(f'Erro: {e}')

Passo a passo

  1. No __init__, atribuímos largura e altura usando self.largura = ... e self.altura = ..., acionando os setters para validar.
  2. O getter de largura retorna self._largura; o setter verifica se o valor é maior que zero e, caso contrário, lança ValueError.
  3. O mesmo padrão é aplicado para altura.
  4. A property area é somente leitura (sem setter) e calcula self._largura * self._altura toda vez que é acessada.
  5. A property perimetro também é somente leitura e calcula 2 * (self._largura + self._altura).
  6. Nos testes, criamos um retângulo 2x3, imprimimos área e perímetro. Depois alteramos a largura para 4 e a área é recalculada automaticamente.
  7. Tentamos atribuir largura negativa, o que dispara o setter e lança a exceção, capturada e impressa.

Por que funciona

As properties largura e altura controlam o acesso aos atributos internos, garantindo que nunca sejam inválidos. As properties area e perimetro são calculadas sob demanda, refletindo sempre os valores atuais. Isso evita inconsistências e mantém o código limpo.

Erros comuns

  • Armazenar área como atributo: se fizer self.area = ... no __init__, a área não será atualizada quando largura ou altura mudarem. Prefira calcular na property.
  • Esquecer de validar no setter: sem validação, valores negativos ou zero podem causar resultados incorretos.
  • Tentar atribuir a area: como não há setter, r.area = 10 gera AttributeError. Isso é esperado para atributos calculados somente leitura.

Outra forma de resolver

Poderia-se usar métodos get_area() e get_perimetro(), mas properties permitem acesso como atributo, mais natural.

Saída esperada

Area: 6
Perimetro: 10
Nova area: 12
Erro: Largura deve ser positiva