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.precoAs 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._precoSetter
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 = valorDeleter (opcional)
Também é possível controlar quando um atributo é removido.
@preco.deleter
def preco(self):
print("Preço removido")
del self._precoQuando 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.
Comentários
Postar um comentário