Implementação do Padrão Singleton em Python

Fundamentos e Motivação

O padrão de projeto Singleton assegura que uma classe possua apenas uma instância ativa durante a execução da aplicação, disponibilizando um ponto de acesso global a esse objeto. Essa estratégia é indicada quando uma classe centraliza funcoinalidades específicas ou gerencia recursos compartilhados, como caches, pools de conexões ou gerenciadores de configuração. Ao impedir a criação de múltiplas instâncias, o padrão otimiza o consumo de memória e garante consistência de estado em todo o sistema.

Aspectos Críticos de Implementação

Ao estruturar um Singleton, três pilares técnicos devem ser avaliados:

  • Concorrência e thread safety: Evitar que múltiplas threads inicializem objetos duplicados simultaneamente.
  • Inicialização tardia (Lazy Loading): Postergar a alocação do objeto até que ele seja realmente requisitado pela aplicação.
  • Custo de acesso: Minimizar o uso de mecanismos de bloqueio após a primeira criação, preservando a latência nas chamadas subsequentes.

Em Python, a linguagem oferece recursos nativos que permitem intecreptar o ciclo de vida das classes. Duas das abordagens mais robustas utilizam metaclasses e decoradores para controlar o processo de instanciação.

Controle via Metaclass

Metaclasses definem como outras classes são construídas. Ao personalizar o método __call__, é possível interceptar a tentativa de criação de objetos e redirecionar o fluxo para uma instância já existente, garantindo unicidade sem modificar a lógica interna da classe alvo.

class SingletonMeta(type):
    _instance_cache = {}

    def __call__(cls, *args, **kwargs):
        if cls not in cls._instance_cache:
            # Aloca o objeto apenas na primeira invocação
            fresh_instance = super().__call__(*args, **kwargs)
            cls._instance_cache[cls] = fresh_instance
        return cls._instance_cache[cls]


class GlobalConfig(metaclass=SingletonMeta):
    def __init__(self):
        self.parameters = {"env": "production", "timeout": 30}


# Validação de identidade
cfg_first = GlobalConfig()
cfg_second = GlobalConfig()

print(f"Endereço cfg_first: {id(cfg_first)}")
print(f"Endereço cfg_second: {id(cfg_second)}")
print(f"Instância única confirmada: {cfg_first is cfg_second}")

Controle via Decorator

Decoradores permitem encapsular a classe em uma função wrapper, mantendo a referência do objeto em um escopo fechado (closure). Essa técnica desacopla a lógica de singleton da definição da classe, facilitando a reutilização em diferentes componentes do projeto.

def enforce_singleton(target_cls):
    _stored_ref = None

    def wrapper(*args, **kwargs):
        nonlocal _stored_ref
        if _stored_ref is None:
            _stored_ref = target_cls(*args, **kwargs)
        return _stored_ref
    
    return wrapper


@enforce_singleton
class AppLogger:
    def __init__(self):
        self.history = []

    def trace(self, msg):
        self.history.append(msg)


# Verificação de comportamento
log_alpha = AppLogger()
log_beta = AppLogger()

print(f"Endereço log_alpha: {id(log_alpha)}")
print(f"Endereço log_beta: {id(log_beta)}")
print(f"Mesma referência na memória: {log_alpha is log_beta}")

Tags: Python singleton metaclasses decorators design-patterns

Publicado em 9-13 11:43