Módulo 10 - Orientação a objetos avançada

property: getter, setter e validação

10 min de leitura · por Cesar Gargiulo, revisado pela equipe ValorFinal e GuardiaSec · Atualizado em 01/07/2026

Velocidade

O que você vai aprender

  • Entender o problema de expor um atributo sem controle.
  • Criar um getter com @property.
  • Criar um setter com validação usando @nome.setter.
  • Manter a interface obj.valor mesmo com lógica por trás.

O problema do atributo livre

Imagine uma classe Produto com um atributo preco. Se ele for um campo público comum, nada impede alguém de escrever produto.preco = -50, e agora o seu sistema tem um produto de preço negativo. Em muitas linguagens, a saída seria esconder o campo e obrigar todo mundo a usar métodos getPreco e setPreco, com a validação dentro do set. Funciona, mas é verboso: quem usa a classe precisa chamar métodos toda vez, e o código fica cheio de parênteses. O Python resolve isso de um jeito mais elegante, sem sacrificar a simplicidade de escrever produto.preco.

A ideia da property é começar simples e só adicionar controle quando precisar, sem quebrar quem já usa a classe. Você pode lançar uma classe com um atributo público comum e, no dia em que surgir a necessidade de validar, transformá-lo em property sem mudar uma linha do código que a consome. A interface externa continua idêntica, produto.preco para ler e produto.preco = 10 para escrever, mas agora há funções suas rodando por trás. Esse é o encapsulamento à moda do Python: cobre a lógica quando ela é necessária, sem cerimônia antecipada.

O getter com @property

O primeiro passo é o getter. Você escreve um método com o nome do atributo e o decora com @property. A partir daí, ao ler obj.preco, o Python chama esse método em vez de devolver um campo. Por convenção, o valor real fica guardado em um atributo com um sublinhado na frente, como _preco, sinalizando que é interno. Repare que quem usa a classe nem percebe: escreve produto.preco, sem parênteses, exatamente como faria com um campo comum. A property também é ótima para valores calculados, que derivam de outros atributos e não precisam ser guardados.

class Circulo:
    def __init__(self, raio):
        self._raio = raio

    @property
    def raio(self):
        return self._raio

    @property
    def area(self):          # valor calculado, sem campo proprio
        return 3.14159 * self._raio ** 2

c = Circulo(2)
print(c.raio)   # 2  (chamou o getter, sem parenteses)
print(c.area)   # 12.56636  (calculado na hora)

@property faz raio e area serem lidos como atributos; area é calculada a cada acesso.

O setter que valida

O poder de verdade aparece no setter. Depois de definir o getter com @property, você cria um segundo método com o mesmo nome do atributo, decorado com @nome.setter, e é ali que a validação mora. Toda vez que alguém escrever produto.preco = valor, o Python chamará esse método, passando o valor. Se ele for inválido, você levanta uma exceção e a atribuição errada nunca acontece. O resultado é uma classe que se defende: é impossível colocá-la em um estado inconsistente pela porta da frente, e mesmo assim a sintaxe de uso permanece a de um campo comum.

class Produto:
    def __init__(self, preco):
        self.preco = preco       # ja passa pelo setter e valida

    @property
    def preco(self):
        return self._preco

    @preco.setter
    def preco(self, valor):
        if not isinstance(valor, (int, float)):
            raise TypeError("preco deve ser numero")
        if valor < 0:
            raise ValueError("preco nao pode ser negativo")
        self._preco = valor

p = Produto(10)
p.preco = 25        # ok, passa na validacao
print(p.preco)      # 25
# p.preco = -5      # ValueError: preco nao pode ser negativo

O setter valida a cada atribuição; até o __init__ usa self.preco para passar pela validação.

Um detalhe elegante do exemplo: o __init__ atribui self.preco = preco, e não self._preco = preco. Com isso, até a criação do objeto passa pela validação do setter, então nem no construtor um valor inválido escapa. Essa é a marca de uma classe bem projetada no avançado: ela garante suas próprias invariantes, aquelas regras que precisam ser sempre verdadeiras, como preço não negativo. A property centraliza essa garantia em um único lugar, em vez de espalhar validações por todo o código que mexe no objeto.

Teste rápido

Qual a principal vantagem de usar @property com setter em vez de deixar o atributo público?

Perguntas frequentes

Devo transformar todo atributo em property desde o início?
Não. A filosofia pythônica é começar com atributos públicos simples e só criar property quando surgir a necessidade de validar ou calcular. Como a interface de acesso não muda, você pode fazer essa transformação depois, sem quebrar o código que já usa a classe. Encapsular cedo demais só adiciona ruído.
O sublinhado em _preco torna o atributo privado de verdade?
Não no sentido de outras linguagens. O sublinhado é uma convenção que sinaliza uso interno; o Python não impede o acesso. A property é o que dá controle real, interceptando leitura e escrita. O sublinhado apenas comunica a intenção de que _preco é detalhe interno, não parte da interface.
Uma property sem setter serve para quê?
Vira um atributo somente leitura: dá para ler obj.valor, mas tentar atribuir levanta erro. É útil para valores calculados, como a área de um círculo, ou para expor um dado interno que não deve ser alterado de fora. Você adiciona o setter só se e quando a escrita fizer sentido.
Posso ter validação diferente na leitura e na escrita?
Sim. O getter e o setter são métodos separados, cada um com sua lógica. O getter pode formatar ou calcular o valor devolvido, e o setter pode validar ou transformar o valor recebido. Essa separação é justamente a vantagem de interceptar as duas operações de forma independente.
Property tem custo de desempenho?
Cada acesso vira uma chamada de método, o que é um pouco mais caro que ler um campo direto, mas o custo é pequeno e raramente relevante. Só se torna algo a considerar em laços muito quentes, e mesmo assim a clareza e a segurança que a property traz costumam compensar. Meça antes de otimizar.

Fontes

Seu progresso fica salvo neste aparelho. Assinantes sincronizam entre os aparelhos.