Czy istnieje prosty, elegancki sposób definiowania singletonów? [duplikat]

To pytanie ma już odpowiedź tutaj:

Istnieje wiele sposobów definiowania singletonów w Pythonie. Czy istnieje konsensus co do przepełnienia stosu?

Author: martineau, 2008-08-28

22 answers

Naprawdę nie widzę potrzeby, ponieważ moduł z funkcjami (a nie klasą) dobrze by służył jako singleton. Wszystkie jego zmienne byłyby powiązane z modułem, który i tak nie mógł być wielokrotnie inicjowany.

Jeśli chcesz użyć klasy, nie ma sposobu na tworzenie prywatnych klas lub prywatnych konstruktorów w Pythonie, więc nie możesz chronić przed wieloma instancjami, innymi niż tylko poprzez konwencję w użyciu Twojego API. Ja bym jeszcze wstawił metody w module i rozważał moduł jako singleton.

 316
Author: Staale,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-13 20:44:16

Oto moja własna implementacja singletonów. Wszystko, co musisz zrobić, to udekorować klasę; aby uzyskać singleton, musisz użyć metody Instance. Oto przykład:

@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

A oto kod:

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)
 273
Author: Paul Manta,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-05 19:57:19

Możesz nadpisać __new__ metodę w następujący sposób:

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"
 174
Author: jojo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-12 01:59:28

Nieco innym podejściem do implementacji Singletona w Pythonie jest wzór Borga autorstwa Alexa Martelli (pracownik Google i geniusz Pythona).

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

Więc zamiast zmuszać wszystkie instancje do posiadania tej samej tożsamości, dzielą stan.

 104
Author: Peter Hoffmann,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-13 20:46:02

Podejście modułowe działa dobrze. Jeśli absolutnie potrzebuję Singletona, wolę podejście Metaclass.

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
 75
Author: Acuminate,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-13 02:50:29

Zobacz tę implementację z PEP318 , implementującą wzór Singletona za pomocą dekoratora:

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

@singleton
class MyClass:
    ...
 41
Author: Wei,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-14 21:21:09

Jak mówi przyjęta ODPOWIEDŹ, najbardziej idiomatycznym sposobem jest po prostu użycie modułu.

W związku z tym, oto dowód koncepcji:]}
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

Zobacz model danych Pythona , aby uzyskać więcej informacji na temat __new__.

Przykład:

@singleton
class Duck(object):
    pass

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

Uwagi:

  1. W tym celu musisz użyć klas nowego stylu (derive from object).

  2. Singleton jest inicjowany, gdy jest zdefiniowany, a nie po raz pierwszy używany.

  3. To tylko zabawkowy przykład. Nigdy nie użyłem tego w kodzie produkcyjnym i nie planuję.

 26
Author: Lambda Fairy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:33:24

Kiedy napisałem singleton w Pythonie, użyłem klasy, w której wszystkie funkcje członkowskie miały dekorator classmethod.

class foo:
  x = 1

  @classmethod
  def increment(cls, y = 1):
    cls.x += y
 14
Author: David Locke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-21 18:44:06

Dokumentacja Pythona obejmuje to:

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

Pewnie bym to przepisał, żeby wyglądało bardziej tak:

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

Powinno być stosunkowo czyste, aby rozszerzyć to:

class Bus(Singleton):
    def init(self, label=None, *args, **kwds):
        self.label = label
        self.channels = [Channel("system"), Channel("app")]
        ...
 13
Author: Brian Bruggeman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-14 21:26:51

Nie jestem tego pewien, ale mój projekt używa 'Convention singletons' (nie wymuszonych singletonów), to znaczy, jeśli mam klasę o nazwie DataController, definiuję to w tym samym module:

_data_controller = None
def GetDataController():
    global _data_controller
    if _data_controller is None:
        _data_controller = DataController()
    return _data_controller
To nie jest eleganckie, ponieważ jest pełne sześć linijek. Ale wszystkie moje singletony używają tego wzoru i jest on przynajmniej bardzo wyraźny (co jest pythoniczne).
 12
Author: u0b34a0f6ae,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-20 19:56:46

Na blogu Google Testing jest też kilka ciekawych artykułów, które omawiają, dlaczego singleton jest / może być zły i jest anty-wzorcem:

 9
Author: FrankS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-01 09:24:09

Tworzenie dekoratora singleton (aka adnotacja) to elegancki sposób, jeśli chcesz ozdobić (adnotować) klasy w przyszłości. Następnie po prostu umieść @singleton przed definicją klasy.

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

@singleton
class MyClass:
    ...
 9
Author: Matt Alcock,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-22 22:16:50

Oto przykład z Pythona IAQ Petera Norviga Jak zrobić wzór Singletona w Pythonie? (powinieneś użyć funkcji wyszukiwania w przeglądarce, aby znaleźć to pytanie, nie ma bezpośredniego linku, przepraszam)

Również Bruce Eckel ma inny przykład w swojej książce Thinking in Python (znowu nie ma bezpośredniego linku do kodu)

 7
Author: Serge,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-28 09:28:49

Myślę, że zmuszanie klasy lub instancji do bycia singletonem jest przesadą. Osobiście lubię definiować normalną instancjonalną klasę, semi-prywatne odniesienie i prostą funkcję fabryczną.

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

Lub jeśli nie ma problemu z tworzeniem instancji po pierwszym zaimportowaniu modułu:

class NothingSpecial:
    pass

THE_ONE_AND_ONLY = NothingSpecial()

W ten sposób można pisać testy na świeżych instancjach bez skutków ubocznych i nie ma potrzeby posypywania modułu instrukcjami globalnymi, a w razie potrzeby można uzyskać warianty w przyszłości.

 5
Author: Mark Evans,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-14 21:23:00

Wzorzec Singletona zaimplementowany w Pythonie dzięki uprzejmości ActiveState.

Wygląda na to, że sztuką jest umieszczenie klasy, która powinna mieć tylko jedną instancję wewnątrz innej klasy.

 3
Author: Mark Biek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-03 20:54:25

Singleton może być dobry lub zły, wiem. Jest to moja implementacja, i po prostu rozszerzyć klasyczne podejście do wprowadzenia pamięci podręcznej wewnątrz i produkować wiele instancji innego typu lub, wiele instancji tego samego typu, ale z różnymi argumentami.

Nazwałem go Singleton_group, ponieważ grupuje podobne instancje razem i uniemożliwia utworzenie obiektu tej samej klasy z tymi samymi argumentami:

# 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>}

Każdy obiekt posiada singleton cache... To może być zły, ale dla niektórych świetnie się sprawdza:)

 3
Author: Mychot sad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-14 21:25:49
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
 2
Author: John Mee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-26 01:53:38

Moje proste rozwiązanie, które opiera się na domyślnej wartości parametrów funkcji.

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

class Context(object):
    # Anything you want here
 2
Author: Tiezhen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-28 13:15:24

Jako stosunkowo nowy w Pythonie nie jestem pewien, jaki jest najczęstszy idiom, ale najprostszą rzeczą, jaką przychodzi mi do głowy, jest użycie modułu zamiast klasy. To, co byłoby instancjami na twojej klasie, staje się tylko funkcjami w module, a wszelkie dane stają się zmiennymi w module zamiast członkami klasy. Podejrzewam, że jest to podejście pythoniczne do rozwiązania rodzaju problemu, że ludzie używają singletons dla.

Jeśli naprawdę chcesz klasy singleton, jest rozsądna implementacja opisana na pierwszy hit w Google dla "Python singleton", konkretnie:

class Singleton:
    __single = None
    def __init__( self ):
        if Singleton.__single:
            raise Singleton.__single
        Singleton.__single = self
To chyba wystarczy.
 2
Author: John,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-13 20:45:18

Przyrodni brat Singletona

Całkowicie zgadzam się ze staale ' em i zostawiam tu przykład tworzenia singletonowego przyrodniego brata:

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

a zgłosi teraz, że należy do tej samej klasy co singleton, nawet jeśli nie wygląda na to. Więc singletony używające skomplikowanych klas kończą się w zależności od tego, że nie zadzieramy z nimi za dużo.

Będąc tak, możemy mieć ten sam efekt i używać prostszych rzeczy, takich jak zmienna lub moduł. Mimo to, jeśli chcemy użyć klas dla jasności i ponieważ w Pythonie klasa jest obiektem , więc mamy już obiekt (nie instancję i, ale zrobi to tak samo).

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

Tam mamy ładny błąd twierdzenia, jeśli spróbujemy utworzyć instancję, i możemy przechowywać na derywacjach statycznych członków i wprowadzać do nich zmiany w czasie wykonywania (kocham Pythona). Ten obiekt jest tak dobry, jak inne o przyrodnich braci (nadal można je tworzyć, jeśli chcesz), jednak będzie działać szybciej ze względu na prostotę.

 2
Author: neu-rah,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-14 21:30:33
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))
 1
Author: Volodymyr Pavlenko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-15 07:08:55

W przypadkach, gdy nie chcesz powyższego rozwiązania opartego na metaclass i nie podoba Ci się proste podejście oparte na dekoratorze funkcji (np. ponieważ w takim przypadku statyczne metody na klasie singleton nie będą działać), ten kompromis działa:

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)
 0
Author: ithkuil,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-14 21:23:48