Domine o @property em Python: escreva classes mais seguras e elegantes

Quando começamos a trabalhar com Programação Orientada a Objetos (POO) em Python, é comum acessar e modificar atributos diretamente:

class Produto:
def __init__(self, nome, preco):
self.nome = nome
self.preco = preco produto = Produto("Notebook", 4500)
print(produto.preco) produto.preco = -100

O problema é que nada impede que um preço inválido seja atribuído ao objeto.

É aí que entra o @property, um dos recursos mais elegantes da linguagem para encapsular atributos sem complicar a forma como eles são utilizados.


O que é @property?

O decorator @property transforma um método em um atributo somente leitura (ou controlado), permitindo que você execute validações e lógica de negócio sempre que um valor for acessado ou alterado.

Na prática, você continua usando:

produto.preco

em vez de:

produto.get_preco()

Isso torna a API da classe muito mais limpa e intuitiva.


Impedindo acesso a variáveis - Um Exemplo Interessante

import hashlib
import os

class User:
    def __init__(self, name, password):
        self.name = name
        self.password = password

@property
def password(self):
    raise AttributeError("Password is write-only")

@password.setter
def password(self, plaintext):
    salt = os.urandom(32)
    self._hashed_password = hashlib.pbkdf2_hmac(
        "sha256", plaintext.encode("utf-8"), salt, 100_000
    )

Por que usar um atributo com "_"?

No exemplo utilizamos:

self._preco


O underscore (_) indica por convenção que aquele atributo é interno da classe.

Não é uma proteção real como em outras linguagens, mas serve para informar aos desenvolvedores que aquele atributo não deve ser manipulado diretamente.

O acesso deve ocorrer através da propriedade:

produto.preco

As três partes do @property

Uma propriedade normalmente possui três componentes.

Getter

Responsável por retornar o valor.

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

Setter

Executa validações antes de alterar o valor.

@preco.setter
def preco(self, valor):
    if valor < 0:
        raise ValueError("Preço inválido")
    self._preco = valor

Deleter (opcional)

Também é possível controlar quando um atributo é removido.

@preco.deleter
def preco(self):
    print("Preço removido")
    del self._preco

Quando vale a pena utilizar?

O @property é uma excelente escolha quando você precisa:

  • validar dados antes de armazená-los;
  • impedir valores inválidos;
  • calcular valores dinamicamente;
  • manter compatibilidade entre versões da classe;
  • esconder detalhes internos da implementação.

Um exemplo com validação de dados

Imagine um funcionário cuja idade nunca pode ser negativa.

class Funcionario:
def __init__(self, nome, idade):
self.nome = nome
self.idade = idade

@property
def idade(self):
return self._idade

@idade.setter
def idade(self, valor):
if valor < 0:
raise ValueError("Idade inválida.")
self._idade = valor

Uso:

f = Funcionario("Maria", 30)

print(f.idade) f.idade = 31 f.idade = -5

O código permanece simples para quem utiliza a classe, mas seguro para quem a desenvolve.


Mais um exemplo prático de validação de dados

class Produto:
def __init__(self, nome, preco):
self.nome = nome
self._preco = preco

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

@preco.setter
def preco(self, valor):
if valor < 0:
raise ValueError("O preço não pode ser negativo.")
self._preco = valor

Agora podemos utilizar a classe normalmente:

produto = Produto("Notebook", 4500)
print(produto.preco) produto.preco = 5200
print(produto.preco) produto.preco = -100

Saída:

4500
5200
ValueError: O preço não pode ser negativo.

Perceba que, para quem usa a classe, parece que estamos acessando um atributo comum. Entretanto, existe uma validação sendo executada "por trás dos bastidores". Esse padrão é amplamente recomendado na documentação e em tutoriais sobre Python.


Conclusão

O @property é um dos recursos que tornam o Python tão elegante. Ele permite encapsular regras de negócio sem obrigar quem utiliza sua classe a chamar métodos como get_preco() ou set_preco().

Sempre que um atributo precisar de validação, cálculo ou qualquer lógica adicional, considere utilizar @property. Seu código ficará mais organizado, mais seguro e muito mais "Pythonic", preservando uma interface simples enquanto adiciona controle interno.


Referências

Comentários

Postagens mais visitadas deste blog

Como exibir informações e metadados de DataFrames do PySpark

Calculadora de Juros Compostos

Como utilizar o PySpark no Google Colab