11 de novembro de 2012

Singleton simples em Python

Sem entrar no mérito de quando usar ou não Singleton, quero apenas mostrar o poder de implementar esse conceito em Python.

Eu já vi algumas implementações diferentes de Singleton em Python, mas até agora a mais simples de todas foi a que o Alex Martelli mostrou em sua palestra The Python Object Model. Vale a pena assistir a essa apresentação.

Segue abaixo:
>>> class Singleton(object):
...     def __new__(cls, *args, **kwargs):
...         if not hasattr(cls, '_instance'):
...             cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
...         return cls._instance
...

Agora, experimente instanciar objetos Singleton:
>>> a = Singleton()
>>> b = Singleton()
>>> a.numero = 1
>>> print a.numero, b.numero
1 1
>>> b.numero += 1
>>> print a.numero, b.numero
2 2 
>>>

Viu? O que se faz no objeto a, reflete no objeto b. Na verdade, os objetos a e b não são iguais. Eles são o mesmo objeto. Em outras palavras, não são gêmeos idênticos. Eles são a mesma pessoa.

Para conferir, dê um print id(a) e print id(b) para ver o mesmo endereço de memória alocada para eles.

Está aí, conforme prometido, um singleton simples em Python

Editado em 13/nov/2012: eu encontrei hoje o post Singleton and Unit Tests, do Felipe Prenholato, que mostra uma implementação de Singleton que pode ser destruído.

Eu sou Vinicius Assef, um programador do século passado que gosta de Python, pratica Lean Development e acredita em Deus. Você pode me contactar por email ou twitter.

7 comentários:

  1. Ótima implementação Vinicius !
    Simples e fácil...

    Ótima dica!

    ResponderExcluir
  2. Bonito exemplo...e errado.

    O método que você tem que implementar pra isso funciona r'e "__new__" e não "__init__" -- O método "__init__" semrpe deve retornar None (isso é, não tem retorno)
    Voce deve ter testado isso com "__new__", e mudou o código antes de postar aqui - por que está chamando o método '__new__' de super

    ResponderExcluir
    Respostas
    1. João, obrigado pela excelente observação.

      Fui no automático e acabei usando __init__() ao invés de __new__(). Já está corrigido.

      Sinta-se à vontade para corrigir qualquer erro que encontrar nesse blog.

      Novamente, muito obrigado.

      Excluir
  3. Olá,

    uma coisa que é legal pensar antes de usar o singleton, é que ele foi é um padrão criado para linguagem que tem apenas OO como paradigma de programação.

    Como Python é multi paradigma é possível ter um resultado semelhante criando uma instância em um módulo, e usar sempre essa instância.

    Valeu!

    ResponderExcluir
    Respostas
    1. Andrews, vc poderia colocar um exemplo?

      Excluir
    2. Acredito que seja uma "variavel de módulo", e é realmente muito simples, a diferença é que você não utiliza a classe no import (pode usar, mas nao se comportará como um singleton), mas sim a variavel de seu tipo. Ex:

      ---------------
      # mySingletons.py

      class Hello:
      def __init__(self):
      self.name = ''

      def __str__(self):
      return f'Hello {self.name}!'


      if not hasattr(globals(), 'hello'):
      hello = Hello()

      ------------
      # secondScript.py

      from mySingletons import hello

      print(hello)
      ------------
      main.py

      from mySingletons import hello

      my_hello = hello
      my_hello.name = 'Ronald'

      print(my_hello)

      import secondScript

      # saída
      >> 'Hello Ronald!'

      >> 'Hello Ronald!'

      pela instância ser criada apenas ao não ser encontrada em globals(), comportamentos estranhos podem acontecer ao se declarar variaveis de mesmo nome do singleton. Porém é uma alternativa, excede em simplicidade, mas peca um pouco na confiabilidade (a depender de sua rotina).

      Excluir
    3. Detalhe, pela indentação quebrada, devo ressaltar: O "if not hasattr" em mySingletons é fora da classe Hello, ou seja, no escopo padrão do script. Pois é ele que se encarrega de instanciar ou não, a variavel hello a ser importada

      Excluir

Marcadores