Padrão Singleton e Técnicas Avançadas em Python

  1. 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)

Tags: Python singleton metaclasse __call__ decoradores

Publicado em 7-17 11:36