Encapsulamento em Python

python
0 – Python
5 – Orientada a Objetos (POO)
5.1 – Classes e objetos
5.2 – Herança (simples e múltipla)
5.3 – Polimorfismo
5.4 – Encapsulamento (público, _protegido, __privado)
5.5 – Métodos mágicos (__init__, __call__, __add__, etc.)
5.6 – Propriedades (@property, setter, deleter)
5.7 – Classes abstratas (ABC, abstractmethod)
5.8 – Metaclasses (type, __new__)
LEGENDA
Nivel_1
Nivel_2
Nivel_3

Encapsulamento esconde detalhes internos de uma classe. Ele protege dados contra alterações indevidas e acidentais. Primeiramente, Python usa convenções de nomenclatura para indicar acesso. Por exemplo, nome indica público, _nome indica protegido. Além disso, __nome indica privado com name mangling. Assim, você comunica claramente sua intenção aos outros programadores. Consequentemente, o código se torna mais seguro e previsível. Quando utilizar encapsulamento? Em atributos que exigem validação. Também quando você quer manter uma interface estável. Por outro lado, para dados simples, atributos públicos bastam. Python não impõe restrições rígidas como Java ou C++. Portanto, respeitar as convenções depende da sua disciplina. Então, vamos explorar cada nível com exemplos práticos. Três subtítulos guiarão você pelos níveis de encapsulamento. Finalmente, você projetará classes mais seguras e robustas.

Atributos públicos: acesso livre e direto

Atributos públicos não têm underscore no início. Você pode acessá-los e modificá-los livremente de fora. Quando usar atributos públicos? Em dados simples e sem restrições. Por exemplo, coordenadas de um ponto ou cores de uma tela. Nenhuma validação ocorre automaticamente nesses casos. Exemplo de atributos públicos:

Atributos públicos oferecem simplicidade e direção. Eles funcionam bem para dados que não precisam de proteção.

Atributos protegidos: convenção de não acessar

Atributos protegidos usam um underscore prefixo (_atributo). Essa convenção significa “não toque a menos que saiba o que faz”. O interpretador Python não impõe nenhuma restrição real. Quando usar atributos protegidos? Em herança e desenvolvimento interno. Use-os para indicar detalhes de implementação. A comunidade respeita essa convenção amplamente. Exemplo de atributos protegidos:

Atributos protegidos funcionam como uma convenção social. Eles ajudam muito em bibliotecas e frameworks.

Atributos privados: name mangling e encapsulamento real

Atributos privados usam dois underscores prefixo (__atributo). Python aplica name mangling: renomeia para _Classe__atributo. Isso dificulta o acesso acidental de fora da classe. Quando usar atributos privados? Em dados sensíveis ou internos. Use-os também para evitar conflitos de nome em herança. O interpretador altera o nome automaticamente. Exemplo de atributos privados:

Atributos privados oferecem o encapsulamento mais forte em Python. A fórmula da proteção segue esta hierarquia: \(P = \text{público} < \text{protegido} < \text{privado}[/latex] Use público para dados simples e sem restrições. Use protegido para comunicação interna entre classes mãe e filha. Use privado para dados sensíveis ou implementação interna. Portanto, respeite as convenções e seu código ficará mais seguro.

Métodos Mágicos em Python: O Poder dos Dunder Methods

Métodos mágicos são funções especiais com underscores duplos. Eles definem como objetos se comportam com operadores nativos. Primeiramente, esses métodos começam e terminam com __. Por exemplo, __init__ constrói objetos, __str__ os exibe. Além disso, __add__ define o operador + para sua classe. A voz passiva é usada aqui: “esses métodos são chamados automaticamente pelo Python”. Quando utilizar métodos mágicos? Para tornar objetos mais naturais. Também para integrar suas classes com a linguagem. Python possui dezenas desses métodos para diferentes propósitos. Vamos explorar os mais importantes com exemplos práticos. Três subtítulos guiarão você pelos principais métodos mágicos. Ao final, você criará classes que parecem tipos nativos.

Construtores, representações e chamadas

__init__ inicializa uma nova instância da classe. __new__ controla a criação do objeto (mais raro). __str__ retorna string amigável para usuários. __repr__ retorna string para depuração (deve recriar objeto). __call__ permite chamar o objeto como uma função. Quando usar cada um? __init__ em praticamente toda classe. __str__ para exibição, __repr__ para logs. __call__ para objetos que se comportam como funções. A voz passiva é aplicada: “a string é gerada automaticamente ao imprimir”. Exemplo desses métodos:

Métodos mágicos de construção tornam suas classes profissionais. __repr__ deve ser o mais explícito possível para depuração.

Operadores aritméticos e comparações

__add__ define +, __sub__ define -. __mul__ define *, __truediv__ define /. __eq__ define ==, __lt__ define <. Quando usar esses métodos? Para criar tipos numéricos personalizados. Por exemplo, vetores, matrizes, dinheiro ou frações. A voz passiva é aplicada: “as operações são sobrecarregadas pelos métodos”. Exemplo completo com operadores aritméticos:

Operadores aritméticos tornam suas classes intuitivas. Use NotImplemented para operações não suportadas.

Métodos de contêiner e gerenciamento de contexto

__len__ define o tamanho (chamado por len()). __getitem__ permite acesso por índice (obj[i]). __setitem__ permite atribuição por índice. __contains__ define o operador in. __enter__ e __exit__ criam gerenciadores de contexto. Quando usar esses métodos? Para criar coleções personalizadas. Também para recursos que precisam de inicialização e limpeza. A voz passiva é aplicada: “o recurso é adquirido e liberado automaticamente”. Exemplo de contêiner e context manager:

Métodos de contêiner tornam suas coleções nativas. A fórmula de utilidade dos métodos mágicos: [latex]U = \frac{N_{\text{métodos implementados}}}{N_{\text{comportamentos nativos}}} \times 100\%\) Quanto mais métodos mágicos, mais natural sua classe. Comece com __init__, __str__ e __repr__. Depois adicione operadores e métodos de contêiner conforme necessário. Suas classes parecerão tipos nativos da linguagem.

Polimorfismo em Python

python
0 – Python
5 – Orientada a Objetos (POO)
5.1 – Classes e objetos
5.2 – Herança (simples e múltipla)
5.3 – Polimorfismo
5.4 – Encapsulamento (público, _protegido, __privado)
5.5 – Métodos mágicos (__init__, __call__, __add__, etc.)
5.6 – Propriedades (@property, setter, deleter)
5.7 – Classes abstratas (ABC, abstractmethod)
5.8 – Metaclasses (type, __new__)
LEGENDA
Nivel_1
Nivel_2
Nivel_3

Polimorfismo significa “muitas formas” em grego. Ele permite que objetos diferentes respondam à mesma mensagem. Primeiramente, isso torna o código mais flexível e extensível. Por exemplo, cachorro.falar() e gato.falar() têm resultados diferentes. Além disso, você pode tratar objetos distintos de forma uniforme. Assim, a complexidade do sistema diminui significativamente. Consequentemente, a manutenção se torna muito mais fácil. Quando utilizar polimorfismo? Em sistemas que precisam de extensibilidade. Também quando você quer código que funcione com tipos futuros. Por outro lado, para scripts pequenos, polimorfismo é exagero. Python implementa polimorfismo de forma natural e flexível. Então, vamos explorar três tipos principais com exemplos práticos. Três subtítulos guiarão você pelo polimorfismo em Python. Portanto, ao final, você escreverá código mais genérico e reutilizável.

Polimorfismo por herança (subtipagem)

Classes filhas podem sobrescrever métodos da classe mãe. Uma variável do tipo mãe pode receber qualquer filha. Quando usar polimorfismo por herança? Em hierarquias claras. Por exemplo, um sistema de formas geométricas. Além disso, a herança fornece uma estrutura sólida. Exemplo de polimorfismo por herança:

O mesmo código imprimir_informacoes funciona com qualquer forma. Novas formas podem ser adicionadas sem modificar a função. Portanto, o sistema cresce sem quebrar o existente.

Polimorfismo por duck typing

Python usa tipagem pato: “se anda como pato, é um pato”. O tipo do objeto não importa, apenas seus métodos. Quando usar duck typing? Em código que prioriza flexibilidade. Por exemplo, funções que aceitam qualquer iterável. Assim, você obtém máxima reutilização de código. Exemplo de duck typing:

Duck typing é extremamente flexível e pythonico. Ele prioriza interfaces implícitas sobre herança explícita. Portanto, você escreve menos código com mais resultado.

Polimorfismo com métodos especiais

Métodos especiais (dunder) permitem polimorfismo nativo. Por exemplo, __add__ define o comportamento de +. Quando usar métodos especiais? Para tornar objetos mais naturais. Também para integrar seu código com a linguagem. Além disso, a legibilidade do código melhora muito. Exemplo de polimorfismo com métodos especiais:

Métodos especiais tornam seus objetos cidadãos de primeira classe. A fórmula do polimorfismo pode ser expressa assim: \(P = \frac{N_{\text{tipos suportados}}}{N_{\text{código repetido}}}\) Polimorfismo reduz drasticamente a duplicação de código. Portanto, domine essa técnica para sistemas verdadeiramente flexíveis. Seu código será mais limpo, curto e elegante. Finalmente, pratique com exemplos reais do seu dia a dia.