Existe uma maneira simples e elegante para definir singletons?

votos
363

Parece haver muitas maneiras de definir singletons em Python. Existe uma opinião consensual sobre Stack Overflow?

Publicado 28/08/2008 em 10:03
fonte usuário
Em outras línguas...                            


22 respostas

votos
301

Eu realmente não vejo a necessidade, como um módulo com funções (e não uma classe) serviria bem como um singleton. Todas as suas variáveis ​​seria ligado ao módulo, que não pôde ser instanciado várias vezes de qualquer maneira.

Se você deseja usar uma classe, não há nenhuma maneira de criar aulas particulares ou construtores privados em Python, então você não pode proteger contra várias instâncias, que não seja apenas através de convenção em uso de sua API. Eu ainda só colocar métodos em um módulo, e considerar o módulo como o Singleton.

Respondeu 28/08/2008 em 10:10
fonte usuário

votos
263

Aqui está a minha própria implementação de singletons. Tudo que você tem a fazer é decorar a classe; para obter o singleton, então você tem que usar o Instancemétodo. Aqui está um exemplo:

@Singleton
class Foo:
   def __init__(self):
       print 'Foo created'

f = Foo() # Error, this isn't how you get the instance of a singleton

f = Foo.instance() # Good. Being explicit is in line with the Python Zen
g = Foo.instance() # Returns already created instance

print f is g # True

E aqui está o código:

class Singleton:
    """
    A non-thread-safe helper class to ease implementing singletons.
    This should be used as a decorator -- not a metaclass -- to the
    class that should be a singleton.

    The decorated class can define one `__init__` function that
    takes only the `self` argument. Also, the decorated class cannot be
    inherited from. Other than that, there are no restrictions that apply
    to the decorated class.

    To get the singleton instance, use the `instance` method. Trying
    to use `__call__` will result in a `TypeError` being raised.

    """

    def __init__(self, decorated):
        self._decorated = decorated

    def instance(self):
        """
        Returns the singleton instance. Upon its first call, it creates a
        new instance of the decorated class and calls its `__init__` method.
        On all subsequent calls, the already created instance is returned.

        """
        try:
            return self._instance
        except AttributeError:
            self._instance = self._decorated()
            return self._instance

    def __call__(self):
        raise TypeError('Singletons must be accessed through `instance()`.')

    def __instancecheck__(self, inst):
        return isinstance(inst, self._decorated)
Respondeu 08/09/2011 em 10:46
fonte usuário

votos
173

Você pode substituir o __new__método como este:

class Singleton(object):
    _instance = None
    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = super(Singleton, cls).__new__(
                                cls, *args, **kwargs)
        return cls._instance


if __name__ == '__main__':
    s1 = Singleton()
    s2 = Singleton()
    if (id(s1) == id(s2)):
        print "Same"
    else:
        print "Different"
Respondeu 27/11/2009 em 20:37
fonte usuário

votos
101

Uma abordagem ligeiramente diferente para implementar o singleton em Python é o padrão de Borg por Alex Martelli (Funcionário do Google e Python gênio).

class Borg:
    __shared_state = {}
    def __init__(self):
        self.__dict__ = self.__shared_state

Então, ao invés de forçar todas as instâncias para ter a mesma identidade, eles compartilham estado.

Respondeu 28/08/2008 em 15:53
fonte usuário

votos
73

A abordagem módulo funciona bem. Se eu absolutamente necessário um singleton eu prefiro a abordagem metaclasse.

class Singleton(type):
    def __init__(cls, name, bases, dict):
        super(Singleton, cls).__init__(name, bases, dict)
        cls.instance = None 

    def __call__(cls,*args,**kw):
        if cls.instance is None:
            cls.instance = super(Singleton, cls).__call__(*args, **kw)
        return cls.instance

class MyClass(object):
    __metaclass__ = Singleton
Respondeu 28/08/2008 em 20:39
fonte usuário

votos
40

Veja esta implementação de PEP318 , implementar o padrão singleton com um decorador:

def singleton(cls):
    instances = {}
    def getinstance():
        if cls not in instances:
            instances[cls] = cls()
        return instances[cls]
    return getinstance

@singleton
class MyClass:
    ...
Respondeu 02/05/2010 em 03:47
fonte usuário

votos
25

Como a resposta aceita diz, a maneira mais idiomática é apenas para usar um módulo .

Com isso em mente, aqui está uma prova de conceito:

def singleton(cls):
    obj = cls()
    # Always return the same object
    cls.__new__ = staticmethod(lambda cls: obj)
    # Disable __init__
    try:
        del cls.__init__
    except AttributeError:
        pass
    return cls

Veja o modelo de dados Python para mais detalhes sobre __new__.

Exemplo:

@singleton
class Duck(object):
    pass

if Duck() is Duck():
    print "It works!"
else:
    print "It doesn't work!"

Notas:

  1. Você tem que usar classes new-style (derivam object) para isso.

  2. O singleton é inicializado quando é definida, em vez de pela primeira vez ele é usado.

  3. Este é apenas um exemplo de brinquedo. Eu nunca realmente usou isso no código de produção, e não planeja.

Respondeu 12/10/2012 em 01:13
fonte usuário

votos
14

A única vez que escreveu um singleton em Python Eu usei uma classe onde todas as funções de membro tinha o decorador classmethod.

class foo:
  x = 1

  @classmethod
  def increment(cls, y = 1):
    cls.x += y
Respondeu 29/08/2008 em 20:15
fonte usuário

votos
12

Estou muito inseguro sobre isso, mas (singletons9 não são aplicadas meu projeto utilizações 'singletons Convenção', isto é, se eu tiver uma classe chamada DataController, eu definir isso no mesmo módulo:

_data_controller = None
def GetDataController():
    global _data_controller
    if _data_controller is None:
        _data_controller = DataController()
    return _data_controller

Não é elegante, uma vez que é um total de seis linhas. Mas todos os meus singletons usar esse padrão, e é pelo menos muito explícita (que é pythônico).

Respondeu 22/08/2009 em 01:44
fonte usuário

votos
11

A documentação Python faz cobrir este:

class Singleton(object):
    def __new__(cls, *args, **kwds):
        it = cls.__dict__.get("__it__")
        if it is not None:
            return it
        cls.__it__ = it = object.__new__(cls)
        it.init(*args, **kwds)
        return it
    def init(self, *args, **kwds):
        pass

Eu provavelmente reescrevê-lo a olhar mais como este:

class Singleton(object):
    """Use to create a singleton"""
    def __new__(cls, *args, **kwds):
        """
        >>> s = Singleton()
        >>> p = Singleton()
        >>> id(s) == id(p)
        True
        """
        self = "__self__"
        if not hasattr(cls, self):
            instance = object.__new__(cls)
            instance.init(*args, **kwds)
            setattr(cls, self, instance)
        return getattr(cls, self)

    def init(self, *args, **kwds):
        pass

Deve ser relativamente limpa para estender esta:

class Bus(Singleton):
    def init(self, label=None, *args, **kwds):
        self.label = label
        self.channels = [Channel("system"), Channel("app")]
        ...
Respondeu 17/07/2012 em 07:38
fonte usuário

votos
9

Criando um decorador Singleton (aka uma anotação) é uma maneira elegante se você quer decorar (anotar) classes daqui para frente. Então você acabou de colocar @Singleton antes de sua definição de classe.

def singleton(cls):
    instances = {}
    def getinstance():
        if cls not in instances:
            instances[cls] = cls()
        return instances[cls]
    return getinstance

@singleton
class MyClass:
    ...
Respondeu 22/02/2012 em 23:16
fonte usuário

votos
8

Existem também alguns artigos interessantes sobre o blog de testes do Google, discutindo porque singleton são / podem ser ruim e são um anti-padrão:

Respondeu 15/09/2008 em 08:47
fonte usuário

votos
7

Aqui está um exemplo de Peter Norvig Python IAQ Como eu faço o padrão Singleton em Python? (Você deve usar recurso de pesquisa do seu navegador para encontrar esta questão, não há nenhuma ligação direta, sorry)

Além disso Bruce Eckel tem outro exemplo em seu livro Thinking in Python (mais uma vez não há nenhuma ligação direta com o código)

Respondeu 28/08/2008 em 10:13
fonte usuário

votos
5

Eu acho que forçando uma classe ou uma instância para ser um singleton é um exagero. Pessoalmente, eu gostaria de definir uma classe normal de instanciável, uma referência semi-privado, e uma função de fábrica simples.

class NothingSpecial:
    pass

_the_one_and_only = None

def TheOneAndOnly():
    global _the_one_and_only
    if not _the_one_and_only:
        _the_one_and_only = NothingSpecial()
    return _the_one_and_only

Ou se não há nenhum problema com instanciar quando o módulo é importada pela primeira vez:

class NothingSpecial:
    pass

THE_ONE_AND_ONLY = NothingSpecial()

Dessa forma, você pode escrever testes contra casos frescos, sem efeitos colaterais, e não há necessidade para polvilhar o módulo com declarações globais, e se necessário você pode derivar variantes no futuro.

Respondeu 27/07/2011 em 13:00
fonte usuário

votos
3

O padrão Singleton implementado com Python cortesia da ActiveState.

Parece que o truque é colocar a classe que é suposto ter apenas uma instância dentro de outra classe.

Respondeu 03/09/2008 em 21:54
fonte usuário

votos
2

meio-irmão de Singleton

Concordo plenamente com Staale e deixo aqui uma amostra de criar um meio-irmão Singleton:

class void:pass
a = void();
a.__class__ = Singleton

areportará agora como sendo da mesma classe como Singleton mesmo se ele não se parece com ele. Então singletons usando classes complicados acabam dependendo de nós não mexer muito com eles.

Sendo assim, podemos ter o mesmo efeito e usar as coisas mais simples como uma variável ou um módulo. Ainda assim, se quisermos usar classes de clareza e porque em Python uma classe é um objeto , de modo que já temos o objeto (não e exemplo, mas ele vai fazer exatamente como).

class Singleton:
    def __new__(cls): raise AssertionError # Singletons can't have instances

Não temos um erro de declaração bom se nós tentamos criar uma instância, e podemos armazenar no derivações membros estáticos e alterá-los em tempo de execução (eu amo Python). Este objeto é tão bom quanto os outros sobre meio-irmãos (você ainda pode criá-los se quiser), no entanto, tendem a correr mais rápido devido à simplicidade.

Respondeu 11/01/2013 em 16:10
fonte usuário

votos
2

OK, solteirão poderia ser bom ou mau, eu sei. Esta é a minha aplicação, e eu simplesmente estender uma abordagem clássica para introduzir um cache dentro e produzir muitos casos de um tipo diferente ou, muitas instâncias de mesmo tipo, mas com argumentos diferentes.

Chamei-Singleton_group, porque agrupa casos semelhantes e evitar que um objeto da mesma classe, com os mesmos argumentos, poderia ser criado:

# Peppelinux's cached singleton
class Singleton_group(object):
    __instances_args_dict = {}
    def __new__(cls, *args, **kwargs):
        if not cls.__instances_args_dict.get((cls.__name__, args, str(kwargs))):
            cls.__instances_args_dict[(cls.__name__, args, str(kwargs))] = super(Singleton_group, cls).__new__(cls, *args, **kwargs)
        return cls.__instances_args_dict.get((cls.__name__, args, str(kwargs)))


# It's a dummy real world use example:
class test(Singleton_group):
    def __init__(self, salute):
        self.salute = salute

a = test('bye')
b = test('hi')
c = test('bye')
d = test('hi')
e = test('goodbye')
f = test('goodbye')

id(a)
3070148780L

id(b)
3070148908L

id(c)
3070148780L

b == d
True


b._Singleton_group__instances_args_dict

{('test', ('bye',), '{}'): <__main__.test object at 0xb6fec0ac>,
 ('test', ('goodbye',), '{}'): <__main__.test object at 0xb6fec32c>,
 ('test', ('hi',), '{}'): <__main__.test object at 0xb6fec12c>}

Cada objeto carrega o cache Singleton ... Isto poderia ser mal, mas ele funciona muito bem para alguns :)

Respondeu 28/02/2012 em 21:42
fonte usuário

votos
2

Minha solução simples, que é baseado no valor padrão de parâmetros da função.

def getSystemContext(contextObjList=[]):
    if len( contextObjList ) == 0:
        contextObjList.append( Context() )
        pass
    return contextObjList[0]

class Context(object):
    # Anything you want here
Respondeu 28/11/2011 em 14:15
fonte usuário

votos
2
class Singleton(object[,...]):

    staticVar1 = None
    staticVar2 = None

    def __init__(self):
        if self.__class__.staticVar1==None :
            # create class instance variable for instantiation of class
            # assign class instance variable values to class static variables
        else:
            # assign class static variable values to class instance variables
Respondeu 17/12/2009 em 16:10
fonte usuário

votos
2

Sendo relativamente novo para Python Eu não tenho certeza do que o idioma mais comum é, mas a coisa mais simples que posso pensar é apenas usando um módulo em vez de uma classe. O que teria métodos de instância sido em sua classe tornam-se apenas a funções do módulo e quaisquer dados só se torna variáveis ​​no módulo em vez de membros da classe. Eu suspeito que esta é a abordagem pythônico para resolver o tipo de problema que as pessoas usam singletons para.

Se você realmente quer uma única classe, há uma implementação razoável descrito no primeiro hit no Google para "singleton Python", especificamente:

class Singleton:
    __single = None
    def __init__( self ):
        if Singleton.__single:
            raise Singleton.__single
        Singleton.__single = self

Isso parece fazer o truque.

Respondeu 28/08/2008 em 10:09
fonte usuário

votos
1
class Singeltone(type):
    instances = dict()

    def __call__(cls, *args, **kwargs):
        if cls.__name__ not in Singeltone.instances:            
            Singeltone.instances[cls.__name__] = type.__call__(cls, *args, **kwargs)
        return Singeltone.instances[cls.__name__]


class Test(object):
    __metaclass__ = Singeltone


inst0 = Test()
inst1 = Test()
print(id(inst1) == id(inst0))
Respondeu 15/08/2012 em 08:08
fonte usuário

votos
0

Nos casos em que você não quer que a solução baseada metaclass acima, e você não gostam da abordagem baseada decorador simples função (por exemplo, porque nesse caso métodos estáticos na classe singleton não vai funcionar), este compromisso funciona:

class singleton(object):
  """Singleton decorator."""

  def __init__(self, cls):
      self.__dict__['cls'] = cls

  instances = {}

  def __call__(self):
      if self.cls not in self.instances:
          self.instances[self.cls] = self.cls()
      return self.instances[self.cls]

  def __getattr__(self, attr):
      return getattr(self.__dict__['cls'], attr)

  def __setattr__(self, attr, value):
      return setattr(self.__dict__['cls'], attr, value)
Respondeu 28/10/2011 em 10:52
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more