🛡️ 9.1 Encapsulamento e Propriedades
🎯 Objetivo
Aprender a proteger os dados de uma classe utilizando o conceito de Encapsulamento e a utilizar Properties (Propriedades) para criar interfaces limpas e profissionais no Python.
🏗️ O Conceito
Encapsular significa esconder os detalhes internos de uma classe e expor apenas o necessário. Isso é fundamental para que o sistema suporte mudanças sem quebrar: se mudarmos uma regra de negócio, alteramos apenas um lugar (dentro da classe), e não em todos os arquivos que a utilizam.
O Problema do Acesso Direto
No Python, a convenção para sinalizar que um atributo é “privado” ou protegido é prefixá-lo com um underscore (_). Isso alerta outros desenvolvedores de que aquele atributo não deve ser acessado diretamente.
class Conta:
def __init__(self, titular: str, saldo: float):
self._titular = titular # Atributo protegido
self._saldo = saldo # Atributo protegido💻 Mão na Massa
Passo 1: O Estilo Java (Getters e Setters)
Muitas linguagens (como Java) utilizam métodos explícitos para acessar dados. Embora funcione, o código fica verboso e “feio”.
class Conta:
def __init__(self, saldo: float):
self._saldo = saldo
def get_saldo(self) -> float:
return self._saldo
def set_saldo(self, valor: float) -> None:
if valor < 0:
print("Erro: O saldo não pode ser negativo.")
else:
self._saldo = valor
# Uso "feio":
conta = Conta(100.0)
conta.set_saldo(conta.get_saldo() + 50.0)Passo 2: O Estilo Pythônico (Properties)
O Python oferece uma solução elegante chamada Properties. Elas permitem que você acesse um método como se fosse um atributo público, mas mantendo a lógica de validação por trás.
class Conta:
def __init__(self, saldo: float = 0.0):
self._saldo = saldo
@property
def saldo(self) -> float:
"""Getter da propriedade saldo"""
return self._saldo
@saldo.setter
def saldo(self, valor: float) -> None:
"""Setter da propriedade saldo com validação"""
if valor < 0:
print("🛑 Alerta: O saldo não pode ser negativo!")
else:
self._saldo = valor
# Uso limpo e elegante:
conta = Conta(1000.0)
conta.saldo = 500.0 # Chama o @saldo.setter automaticamente
print(f"Saldo atual: R$ {conta.saldo:.2f}") # Chama o @propertyPasso 3: Quando usar Setters?
Nem todo atributo precisa de um setter. Na verdade, para uma conta bancária, o saldo só deveria mudar através de métodos de negócio como depositar() e sacar().
class Conta:
def __init__(self, saldo: float):
self._saldo = saldo
@property
def saldo(self) -> float:
return self._saldo
def depositar(self, valor: float) -> None:
self._saldo += valor
def sacar(self, valor: float) -> None:
if valor <= self._saldo:
self._saldo -= valor
else:
print("❌ Saldo insuficiente.")✅ Resultado Esperado
Ao utilizar Properties, sua classe terá uma interface limpa. O código conta.saldo = -100 acionará a validação imediatamente, protegendo a integridade do objeto sem a necessidade de parênteses extras.
Regra de Ouro
Só crie um
@property.setterse você realmente precisar permitir a alteração direta do valor. Caso contrário, use apenas@property(leitura) e métodos de negócio para modificação.
🚨 Erros Comuns
| Erro | Causa | Solução |
|---|---|---|
RecursionError | Chamar self.saldo = valor dentro do setter def saldo. | O atributo interno deve ter um nome diferente, geralmente self._saldo. |
Esquecer o @property | Tentar usar o setter sem o getter definido. | O @property deve vir antes do @nome.setter. |
🔗 Próximo Capítulo
Agora que você domina o encapsulamento, vamos explorar os Atributos de Classe.