Capítulo 05: Programação Orientada a Objetos: Classes, Métodos, Encapsulamento e @property
Especialização em Sistemas com Python & POO com Type Hints • Capítulo 05
🗺️ Mapa Conceitual do Tópico
flowchart TD
subgraph POO_MODEL["Modelo de Objetos do Python (Data Model)"]
CLASS["Classe como Objeto de Primeira Classe\nInstância da Metaclasse type"]
LIFECYCLE["Ciclo de Vida do Objeto\n__new__(cls) [Alocação] → __init__(self) [Inicialização]"]
SLOTS["Otimização de Memória via __slots__\nEliminação de __dict__ e Redução Drástica de RAM"]
end
subgraph DISPATCH["Tipologias de Métodos e Despacho"]
INST["Métodos de Instância (self)\nManipula o estado mutável do Heap"]
CLASS_M["@classmethod (cls) & typing.Self\nFábricas Polimórficas (Factory Methods)"]
STATIC_M["@staticmethod\nFunções Utilitárias Puras sem Estado"]
end
subgraph ENCAPSULATION["Encapsulamento e Descritores (@property)"]
PEP8["_protegido (Convenção Pública de Respeito)"]
MANGLING["__privado (Name Mangling: _Classe__atributo)"]
PROP["@property [Getter] & @campo.setter [Validação]"]
end
POO_MODEL --> DISPATCH
DISPATCH --> ENCAPSULATION
ENCAPSULATION --> DOMAIN["Modelagem de Domínio Corporativo com Tipagem Estrita"]
style POO_MODEL fill:#ede7f6,stroke:#512da8,stroke-width:2px
style DISPATCH fill:#e1f5fe,stroke:#0288d1,stroke-width:2px
style ENCAPSULATION fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px
🏛️ 1. O Modelo de Objetos do Python e Ciclo de Vida
No modelo de dados do Python (Python Data Model), tudo é um objeto, inclusive as próprias classes, funções e módulos. Toda classe definida pelo usuário é uma instância direta da metaclasse fundamental type.
O Ciclo de Instanciação: __new__ vs __init__
A instanciação de um objeto ocorre em duas etapas coordenadas:
__new__(cls, *args, **kwargs): O método alocador estático. É invocado primeiro, recebe a classecls, requisita memória à PVM e retorna a nova instância dePyObject. É o local adequado para implementar o padrão Singleton ou instanciar tipos imutáveis customizados.__init__(self, *args, **kwargs): O método inicializador. Recebe a instância recém-criada através deselfe atribui valores aos atributos no namespace interno.
from typing import Self
class ConexaoPool:
_instancia: Self | None = None
def __new__(cls, *args: object, **kwargs: object) -> Self:
# Padrão Singleton atômico via __new__
if cls._instancia is None:
cls._instancia = super().__new__(cls)
return cls._instancia
⚙️ 2. Tipologias de Métodos: Instância, Classe e Estáticos
O Python organiza o comportamento em três categorias estritas de métodos:
| Tipo de Método | Decorador | Primeiro Argumento | Acesso a Estado | Caso de Uso Canônico |
|---|---|---|---|---|
| Instância | (Nenhum) | self |
Acessa e muta o estado da instância e da classe. | Comportamentos centrais de negócio (ex.: sacar(), transferir()). |
| Classe | @classmethod |
cls |
Acessa e muta apenas atributos de classe (não vê self). |
Métodos de Fábrica (Factory Methods) e desserialização (from_json(), from_dict()). |
| Estático | @staticmethod |
(Nenhum) | Não acessa nem self nem cls. Função pura acoplada ao namespace da classe. |
Validações algorítmicas, funções de hashing e verificadores de formato. |
Métodos de Fábrica com typing.Self (PEP 673)
No Python 3.11+, o tipo especial Self garante que métodos de classe retornem o tipo exato da subclasse em cenários de herança, garantindo checagem estrita no mypy:
from typing import Self
import json
class ConfiguracaoSistema:
def __init__(self, ambiente: str, debug: bool) -> None:
self.ambiente: str = ambiente
self.debug: bool = debug
@classmethod
def from_json(cls, payload: str) -> Self:
dados = json.loads(payload)
return cls(ambiente=dados["ambiente"], debug=bool(dados["debug"]))
🔒 3. Encapsulamento, Name Mangling e o Descritor @property
Diferente de C++ ou Java, Python não possui as palavras-chave public, protected ou private em nível de linguagem. O encapsulamento repousa sobre convenções semióticas e descritores:
- Convenção de Atributo Protegido (
_nome): Indica aos desenvolvedores e ferramentas estáticas que o membro é de uso interno da classe ou de suas subclasses. - Desfiguração de Nomes (Name Mangling -
__nome): Quando um identificador inicia com dois underscores (sem terminar com dois), o interpretador reescreve seu nome internamente para_NomeDaClasse__nome. O objetivo primário é prevenir colisões de atributos em hierarquias profundas de herança, e não segurança criptográfica. - Descritores de Propriedade (
@property): Expõem métodos computados com a sintaxe transparente de acesso a atributos (obj.saldo), permitindo anexar validações defensivas e controle de invariantes através do setter:
from decimal import Decimal
class ContaCorrente:
def __init__(self, titular: str, limite: Decimal) -> None:
self._titular: str = titular
self._limite: Decimal = Decimal("0.00")
self.limite = limite # Invoca o setter com validação
@property
def limite(self) -> Decimal:
"""Getter: Retorna o limite com acesso somente-leitura se omitir o setter."""
return self._limite
@limite.setter
def limite(self, novo_limite: Decimal) -> None:
"""Setter: Assegura invariantes antes da mutação de estado."""
if novo_limite < Decimal("0.00"):
raise ValueError(f"Limite não pode ser negativo: {novo_limite}")
self._limite = novo_limite
⚡ 4. Otimização de Memória com __slots__
Por padrão, toda instância em Python armazena seus atributos em um dicionário dinâmico chamado __dict__. Embora isso permita adicionar atributos em tempo de execução, gera um desperdício substancial de memória Heap (cerca de 150 a 200 bytes por instância).
Ao declarar __slots__ = ("_numero", "_titular", "_saldo"):
- O interpretador aloca um array estático de ponteiros em C para a instância.
- O dicionário
__dict__deixa de ser criado. - A economia de memória atinge 40% a 65%, ideal para aplicações que manipulam milhões de instâncias simultâneas em memória.