Dominando Decorator @property do Python: Getters & Setters Limpos e Compatíveis
Dominando Decorator @property do Python
Descubra como decorator built-in @property do Python permite escrever getters e setters concisos e manuteníveis mantendo sua API backward compatible.
Decorator @property do Python é ferramenta poderosa para encapsular acesso a atributos. Permite definir lógica customizada que executa sempre que atributo é lido ou escrito, sem forçar chamadores a mudar seu código. Nas seções a seguir percorreremos exemplo real — lidando com conversões de temperatura — mostraremos como impor constraints e explicaremos por que @property é solução idiomática.
Classe Sem Getters e Setters
Suponha que precisamos classe simples que armazena temperatura em Celsius e converte para Fahrenheit:
class Celsius:
def __init__(self, temperature=0):
self.temperature = temperature
def to_fahrenheit(self):
return (self.temperature * 1.8) + 32
Usar é direto:
# Criar novo objeto
human = Celsius()
# Definir temperatura
human.temperature = 37
# Ler temperatura
print(human.temperature)
# Converter para Fahrenheit
print(human.to_fahrenheit())
Saída
37 98.60000000000001
Resultado ponto flutuante é esperado devido à representação binária de decimais. Ao inspecionar human.__dict__, verá que Python armazena valor em entrada de dicionário simples:
>>> human.__dict__
{'temperature': 37}
Adicionando Constraints com Getters e Setters
Objetos reais frequentemente precisam validação. Por exemplo, temperaturas não podem cair abaixo do zero absoluto (-273.15 °C). Forma direta é esconder atributo e expor métodos getter/setter explícitos:
class Celsius:
def __init__(self, temperature=0):
self.set_temperature(temperature)
def to_fahrenheit(self):
return (self.get_temperature() * 1.8) + 32
# getter
def get_temperature(self):
return self._temperature
# setter
def set_temperature(self, value):
if value < -273.15:
raise ValueError("Temperature below -273.15 is not possible.")
self._temperature = value
Agora classe impõe limite inferior, mas qualquer código existente acessando obj.temperature deve ser atualizado para chamar obj.get_temperature() e obj.set_temperature(val). Grande refatoração para codebases grandes.
Além disso, variáveis "privadas" do Python são apenas convenção de nomenclatura — _temperature ainda pode ser acessado diretamente, quebrando encapsulamento. É aqui que @property brilha.
Usando Classe property
Python oferece classe property que amarra funções getter e setter a nome de atributo:
class Celsius:
def __init__(self, temperature=0):
self.temperature = temperature
def to_fahrenheit(self):
return (self.temperature * 1.8) + 32
# getter
def get_temperature(self):
print("Getting value...")
return self._temperature
# setter
def set_temperature(self, value):
print("Setting value...")
if value < -273.15:
raise ValueError("Temperature below -273.15 is not possible")
self._temperature = value
# expor como property
temperature = property(get_temperature, set_temperature)
Com essa configuração, código abaixo se comporta exatamente como classe simples original, mas com validação nos bastidores:
human = Celsius(37)
print(human.temperature) # dispara getter
print(human.to_fahrenheit()) # usa getter internamente
human.temperature = -300 # dispara setter e levanta ValueError
Saída
Setting value... Getting value... 37 Getting value... 98.60000000000001 Setting value... Traceback (most recent call last): ... ValueError: Temperature below -273 is not possible
Note como código acessando temperature permanece inalterado; apenas implementação subjacente ganhou validação.
Dica: Dado real vive em _temperature; temperature é apenas interface conveniente e segura.
Decorator @property
Enquanto classe property funciona, sintaxe de decorator do Python oferece forma mais limpa e legível de alcançar mesmo resultado. Decorator automaticamente cria objeto property e anexa getter e setter ao nome do atributo.
class Celsius:
def __init__(self, temperature=0):
self.temperature = temperature
def to_fahrenheit(self):
return (self.temperature * 1.8) + 32
@property
def temperature(self):
print("Getting value...")
return self._temperature
@temperature.setter
def temperature(self, value):
print("Setting value...")
if value < -273.15:
raise ValueError("Temperature below -273 is not possible")
self._temperature = value
Uso permanece o mesmo:
human = Celsius(37)
print(human.temperature)
print(human.to_fahrenheit())
# Tentando definir temperatura inválida
try:
Celsius(-300)
except ValueError as e:
print(e)
Saída
Setting value... Getting value... 37 Getting value... 98.60000000000001 Setting value... Traceback (most recent call last): ... ValueError: Temperature below -273 is not possible
Usar @property mantém namespace limpo, elimina necessidade de nomes separados para getter/setter, e é padrão recomendado para maioria desenvolvedores Python.
python
- Dominando Tipos de Dados em Python: Guia Prático
- Dominando Operadores em Python: Guia Completo
- Dominando a Declaração Pass em Python: Guia Prático
- Dominando Argumentos de Funções em Python: Parâmetros Posicionais, Nomeados e Padrão
- Dominando Dicionários em Python: Criação, Manipulação e Técnicas Avançadas
- Dominando Herança em Python: Conceitos, Sintaxe e Exemplos Práticos
- Dominando Sobrecarga de Operadores em Python: Guia Prático
- Dominando Iteradores em Python: Como Funcionam e Como Criar os Seus
- Closures em Python Explicados: Como Funções Aninhadas Capturam Variáveis
- Dominando Decorator @property do Python: Getters & Setters Limpos e Compatíveis