1. Os Três Pilares da Programação Orientada a Objetos em Python
Python implementa os paradigmas de POO através de três conceitos fundamentais: - Encapsulamento: Reúne dados e comportamentos em uma única unidade (classe), restringindo o acesso direto a detalhes internos por meio de convenções (como o uso de um sbulinhado prefixado _).
- Herança: Permite que uma classe (subclasse) herde atributos e métodos de outra (superclasse), facilitando a reutilização de lógica. Python suporta herança múltipla.
- Polimorfismo: Capacidade de diferentes objetos responderem à mesma chamada de método de formas distintas. Em Python, isso é frequentemente associado ao Duck Typing.
Exemplificando o Polimorfismo com Duck Typing
Python foca no comportamento do objeto e não no seu tipo explícito. Se ele "caminha como um pato e grasna como um pato", ele é tratado como tal.
class EscritorTexto:
def formatar(self, conteudo):
return conteudo.upper()
class EscritorHtml:
def formatar(self, conteudo):
return f"<p>{conteudo}</p>"
def processar_texto(formatador, texto):
print(formatador.formatar(texto))
# Uso dinâmico sem necessidade de interface explícita
processar_texto(EscritorTexto(), "olá mundo")
processar_texto(EscritorHtml(), "olá mundo")
Uso de Classes Abstratas
Para garantir que subclasses implementem métodos específicos, utilizamos o módulo abc.
from abc import ABC, abstractmethod
class Notificacao(ABC):
@abstractmethod
def enviar(self, mensagem):
pass
class Email(Notificacao):
def enviar(self, mensagem):
return f"Enviando e-mail: {mensagem}"
class SMS(Notificacao):
def enviar(self, mensagem):
return f"Enviando SMS: {mensagem}"
servicos = [Email(), SMS()]
for servico in servicos:
print(servico.enviar("Alerta de sistema"))
2. Identidade (is) vs Igualdade (==)
Embora pareçam similares, operam de formas distintas:
- O operador
==verifica se os valores dos objetos são equivalentes. - O operador
isverifica se ambos os nomes apontam para o mesmo objeto na memória (comparam oid()).
x = [10, 20, 30]
y = [10, 20, 30]
z = x
print(x == y) # True (valores iguais)
print(x is y) # False (objetos diferentes na memória)
print(x is z) # True (apontam para o mesmo endereço)
3. Global Interpreter Lock (GIL)
O GIL é um mutex que protege o acesso aos objetos do Python, impedindo que múltiplas threads executem bytecodes simultaneamente no CPython. Suas prnicipais implicações são:
- Impede o paralelismo real de threads em tarefas intensivas de CPU.
- Não afeta significativamente tarefas de I/O (leitura de arquivos, requisições de rede), pois a thread libera o GIL enquanto aguarda a resposta.
- Para paralelismo de processamento, utiliza-se o módulo
multiprocessing, que cria processos separados com instâncias próprias do interpretador.
4. Tipos Mutáveis e Imutáveis
A mutabilidade define se um objeto pode ser alterado após sua criação sem mudar seu ID de memória:
- Imutáveis:
int,float,str,tuple,bool. Qualquer "alteração" resulta na criação de um novo objeto. - Mutáveis:
list,dict,set,bytearray. Podem ser modificados in-place.
5. Funcionamento do yield
A palavra-chave yield transforma uma função comum em um gerador. Ao contrário do return, ela suspende a execução da função, mantendo seu estado para retomada posterior.
def gerador_sequencia(limite):
n = 0
while n < limite:
yield n
n += 2
for valor in gerador_sequencia(6):
print(valor) # Saída: 0, 2, 4
6. Cópia Rasa (Shallow) vs Cópia Profunda (Deep)
- Shallow Copy: Cria um novo objeto, mas insere referências aos objetos encontrados no original. Se houver listas aninhadas, as alterações nelas afetarão ambos.
- Deep Copy: Cria um novo objeto e, recursivamente, cria cópias de todos os objetos aninhados, tornando-os totalmente independentes.
import copy
original = [[1], [2]]
rasa = copy.copy(original)
profunda = copy.deepcopy(original)
original[0].append(99)
# 'rasa' refletirá a mudança: [[1, 99], [2]]
# 'profunda' permanecerá intacta: [[1], [2]]
7. Geradores vs Iteradores
- Iterador: Qualquer objeto que impelmente o protocolo de iteração através dos métodos
__iter__()e__next__(). - Gerador: Uma forma simplificada de criar um iterador usando funções com
yieldou expressões geradoras. Eles utilizam lazy evaluation (avaliação preguiçosa), economizando memória ao não carregar todos os dados de uma vez.
8. Manipulação de Argumentos Dinâmicos: *args e **kwargs
Estes operadores permitem que funções aceitem um número variável de argumentos:
*args: Coleta argumentos posicionais extras em uma tupla.**kwargs: Coleta argumentos nomeados (keyword arguments) em um dicionário.
def registrar_log(nivel, *mensagens, **metadados):
print(f"[{nivel}] Conteúdo: {mensagens}")
print(f"Metadados adicionais: {metadados}")
registrar_log("INFO", "Erro no sistema", "Tentativa 2", usuario="admin", ip="127.0.0.1")