- Entendendo o Método
__call__em Python
Quando você instancia uma classe com x = Foo(), Python na verdade executa type(Foo).__call__(Foo). O método __call__ é definido pela metaclasse de Foo.
2. Implementando Singleton com __call__
class _Unico(type):
"""Metaclasse que cria uma classe base Singleton."""
_instancias = {}
def __call__(cls, *args, **kwargs):
instancia = cls._instancias.get(cls)
if not instancia or (hasattr(instancia, "_reiniciar") and
isinstance(instancia, cls) and instancia._reiniciar()):
cls._instancias[cls] = super(
_Unico, cls).__call__(*args, **kwargs)
return cls._instancias[cls]
class Unico(_Unico('UnicoMeta', (object,), {})):
def __init__(self, *args, **kwargs):
pass
class Log(Unico):
def __init__(self, *args, **kwargs):
print("Log __init__", args, kwargs)
def _reiniciar(self, *args, **kwargs):
return True
l1 = Log("primeira")
l2 = Log("segunda")
print(l1 is l2) # True
3. Singleton com Classe Base
Outra abordagem popular utiliza _new_ para controlar a criação de instâncias:
class BaseUnico(object):
_instancia = None
def __new__(cls, *args, **kwargs):
if not isinstance(cls._instancia, cls):
cls._instancia = object.__new__(cls, *args, **kwargs)
return cls._instancia
class MinhaClasse(BaseUnico):
def __init__(self, valor):
self.valor = valor
a = MinhaClasse(10)
b = MinhaClasse(20)
print(a.valor, b.valor) # 10 10 (mesma instância)
Decoradores em Python
1. Decoradores com Parâmetros
from functools import wraps
def registrar(prefixo):
def decorador_real(funcao):
@wraps(funcao)
def wrapper(*args, **kwargs):
print(f"{prefixo}: chamando {funcao.__name__}")
resultado = funcao(*args, **kwargs)
print(f"{prefixo}: finalizado")
return resultado
return wrapper
return decorador_real
@registrar("LOG")
def somar(a, b):
return a + b
print(somar(3, 4))
2. Decorador em Métodos com self
from functools import wraps
def enfatizar(metodo):
@wraps(metodo)
def interior(self, *args, **kwargs):
saida = metodo(self, *args, **kwargs)
return saida.upper() + "!"
return interior
class Saudacao:
@enfatizar
def dizer(self, palavra):
return palavra
s = Saudacao()
print(s.dizer("olá")) # OLÁ!
Trabalhando com Tracebacks
Para extrair informações de traceback de objetos de exceção, utilize o módulo traceback do Python:
import traceback
import sys
try:
1/0
except ZeroDivisionError:
tipo, valor, tb = sys.exc_info()
linhas = traceback.format_exception(tipo, valor, tb)
print(''.join(linhas))
Árvore Binária: Percrusos
Exemplo completo com percursos recursivos e iterativos:
class No:
def __init__(self, dado=None, esquerda=None, direita=None):
self.dado = dado
self.esquerda = esquerda
self.direita = direita
# Construindo a árvore
# 1
# / \
# 2 3
# / \ / \
# 4 5 6 7
n4 = No(4); n5 = No(5); n6 = No(6); n7 = No(7)
n2 = No(2, n4, n5); n3 = No(3, n6, n7)
raiz = No(1, n2, n3)
# Percursos recursivos
def pre_ordem(no):
if no:
print(no.dado, end=" ")
pre_ordem(no.esquerda)
pre_ordem(no.direita)
def em_ordem(no):
if no:
em_ordem(no.esquerda)
print(no.dado, end=" ")
em_ordem(no.direita)
def pos_ordem(no):
if no:
pos_ordem(no.esquerda)
pos_ordem(no.direita)
print(no.dado, end=" ")
# Versões iterativas
def pre_ordem_pilha(no):
if not no: return
pilha = [no]
while pilha:
atual = pilha.pop()
print(atual.dado, end=" ")
if atual.direita: pilha.append(atual.direita)
if atual.esquerda: pilha.append(atual.esquerda)
def em_ordem_pilha(no):
pilha = []
atual = no
while pilha or atual:
while atual:
pilha.append(atual)
atual = atual.esquerda
no_pilha = pilha.pop()
print(no_pilha.dado, end=" ")
atual = no_pilha.direita
def nivel_ordem(no):
if not no: return
fila = [no]
while fila:
atual = fila.pop(0)
print(atual.dado, end=" ")
if atual.esquerda: fila.append(atual.esquerda)
if atual.direita: fila.append(atual.direita)
print("Pré-ordem recursiva:")
pre_ordem(raiz)
print("\nEm ordem recursiva:")
em_ordem(raiz)
print("\nPós-ordem recursiva:")
pos_ordem(raiz)
print("\nPré-ordem iterativa:")
pre_ordem_pilha(raiz)
print("\nEm ordem iterativa:")
em_ordem_pilha(raiz)
print("\nNível:")
nivel_ordem(raiz)