Encapsulamento
Esta aula aborda encapsulamento em Python, incluindo convenções de nomenclatura com underscore simples e duplo, name mangling, propriedades e a filosofia 'we are all adults'. O conteúdo é prático e progressivo, com exemplos de código e exercícios.
Encapsulamento é um dos pilares da programação orientada a objetos, que consiste em esconder os detalhes internos de uma classe e expor apenas uma interface controlada. Em Python, o encapsulamento não é imposto rigidamente como em linguagens como Java ou C++; em vez disso, utiliza-se convenções e mecanismos sutis para indicar a intenção de proteger atributos e métodos.
Nesta aula, exploraremos as convenções de nomenclatura com underscore simples (_) e duplo (__), o mecanismo de name mangling, as propriedades (properties) e a filosofia Python de "we are all adults" (todos somos adultos), que enfatiza a responsabilidade do programador em vez de restrições rígidas.
Convenção _ e __
Em Python, não existem modificadores de acesso como private ou protected. Em vez disso, usa-se convenções de nomenclatura para comunicar a intenção:
- Underscore simples no início (_atributo): Indica que o atributo ou método é "protegido" ou interno à classe. É uma convenção, não uma imposição. Programadores são incentivados a não acessar diretamente esses membros fora da classe, mas é possível fazê-lo.
- Underscore duplo no início (__atributo): Indica que o atributo ou método é "privado" e deve ser usado apenas dentro da classe. O Python aplica um mecanismo chamado name mangling para dificultar o acesso acidental.
Exemplo de código:
class Pessoa:
def __init__(self, nome, idade):
self._nome = nome # protegido
self.__idade = idade # privado
def exibir(self):
print(f"Nome: {self._nome}, Idade: {self.__idade}")
p = Pessoa("Alice", 30)
print(p._nome) # Acesso permitido (mas não recomendado)
# print(p.__idade) # AttributeError: 'Pessoa' object has no attribute '__idade'
print(p._Pessoa__idade) # Acesso via name mangling (possível, mas evite)
Note que o acesso a __idade diretamente gera um erro, mas o Python ainda permite acesso via _Classe__atributo. Isso mostra que o encapsulamento é mais uma convenção do que uma barreira.
Name Mangling
Name mangling é o processo pelo qual o Python altera o nome de atributos que começam com duplo underscore (e não terminam com duplo underscore). O interpretador adiciona um underscore seguido do nome da classe na frente do atributo. Por exemplo, __idade em uma classe Pessoa se torna _Pessoa__idade.
Isso é feito para evitar conflitos de nomes em subclasses, não para garantir privacidade. O mecanismo é útil quando você quer que um atributo não seja sobrescrito acidentalmente por uma subclasse.
Exemplo:
class Animal:
def __init__(self):
self.__especie = "Animal"
def mostrar(self):
print(self.__especie)
class Cachorro(Animal):
def __init__(self):
super().__init__()
self.__especie = "Cachorro" # Isso não sobrescreve o atributo da classe pai
def mostrar(self):
print(self.__especie)
a = Animal()
a.mostrar() # Animal
c = Cachorro()
c.mostrar() # Cachorro - mas o atributo __especie de Animal ainda existe
Name mangling é uma ferramenta para evitar acidentes, mas não deve ser usada como mecanismo de segurança.
Propriedades
Propriedades permitem controlar o acesso a atributos de forma elegante, usando métodos getter, setter e deleter. Com @property, você pode definir um método que será acessado como um atributo, mas com lógica adicional.
Exemplo:
class ContaBancaria:
def __init__(self, saldo):
self._saldo = saldo
@property
def saldo(self):
return self._saldo
@saldo.setter
def saldo(self, valor):
if valor < 0:
raise ValueError("Saldo não pode ser negativo")
self._saldo = valor
@saldo.deleter
def saldo(self):
print("Deletando saldo...")
del self._saldo
conta = ContaBancaria(1000)
print(conta.saldo) # 1000
conta.saldo = 1500 # usa o setter
# conta.saldo = -500 # raise ValueError
del conta.saldo # usa o deleter
Propriedades são uma forma pythonica de encapsular atributos, permitindo validação e lógica sem expor métodos getter/setter verbosos.
Filosofia ("we are all adults")
A filosofia Python de "we are all adults" (somos todos adultos) reflete a ideia de que a linguagem confia no programador e não impõe restrições desnecessárias. Em vez de forçar o encapsulamento com palavras-chave como private, Python oferece convenções e mecanismos sutis, esperando que os desenvolvedores usem bom senso.
Isso significa que você pode acessar atributos "privados" se realmente precisar, mas deve evitar fazê-lo. A documentação e as convenções existem para guiar, não para prender. A responsabilidade é do programador em respeitar os contratos definidos pelas classes.
Na prática, use _ para indicar uso interno, __ para evitar conflitos em subclasses, e @property para controle refinado. Lembre-se: o objetivo é tornar o código mais legível e manutenível, não criar barreiras intransponíveis.
Boas Práticas
- Prefira usar
_para atributos que não fazem parte da API pública. - Use
__apenas quando precisar evitar sobrescrita em subclasses. - Utilize propriedades para encapsular lógica de acesso a atributos.
- Documente a interface pública da classe claramente.
- Confie na convenção e não abuse de name mangling para esconder dados.
Referências
- Documentação Python: Variáveis privadas
- Documentação Python: property
- PEP 8 - Guia de Estilo Python
- Real Python: Python @property
- GeeksforGeeks: Encapsulation in Python
Exercícios
Crie uma classe
Funcionariocom atributos privados__nomee__salario. Forneça métodos getter e setter usando propriedades, onde o setter valida que o salário é positivo.✓ Resposta:class Funcionario: def __init__(self, nome, salario): self.__nome = nome self.__salario = salario @property def nome(self): return self.__nome @nome.setter def nome(self, valor): self.__nome = valor @property def salario(self): return self.__salario @salario.setter def salario(self, valor): if valor < 0: raise ValueError("Salário não pode ser negativo") self.__salario = valorExplique o que é name mangling e em que situação ele é útil.
✓ Resposta:Name mangling é o processo pelo qual o Python renomeia atributos que começam com duplo underscore (sem duplo underscore no final) adicionando um underscore e o nome da classe na frente. Por exemplo,
__atributoem uma classeMinhaClassese torna_MinhaClasse__atributo. Isso é útil para evitar conflitos de nomes em subclasses, garantindo que um atributo da classe base não seja sobrescrito acidentalmente por uma subclasse.O que significa a filosofia "we are all adults" em Python? Dê um exemplo de como ela se aplica ao encapsulamento.
✓ Resposta:A filosofia "we are all adults" significa que Python confia no programador para usar os recursos corretamente, sem impor restrições rígidas. No encapsulamento, isso se manifesta ao usar convenções como
_e__em vez de palavras-chaveprivateouprotected. O programador é responsável por respeitar a interface pública e não acessar atributos privados desnecessariamente.Considere a classe abaixo. Qual a diferença entre os atributos
_ae__b? O que acontece se uma subclasse definir um atributo__b?class Base: def __init__(self): self._a = 1 self.__b = 2✓ Resposta:_aé uma convenção para indicar que o atributo é protegido, mas pode ser acessado diretamente.__bsofre name mangling, tornando-se_Base__b. Se uma subclasse definir__b, ela criará um novo atributo_Subclasse__b, sem sobrescrever o da classe base. Isso evita conflitos.Escreva uma classe
Termometroque armazena a temperatura em Celsius internamente (_celsius), mas expõe uma propriedadetemperaturaque permite obter e definir o valor em Fahrenheit. O setter deve converter Fahrenheit para Celsius.✓ Resposta:class Termometro: def __init__(self, celsius=0): self._celsius = celsius @property def temperatura(self): return self._celsius * 9/5 + 32 @temperatura.setter def temperatura(self, fahrenheit): self._celsius = (fahrenheit - 32) * 5/9