Is er een eenvoudige, elegante manier om singletons te definiëren?

Er lijken veel manieren te zijn om singletonsin Python te definiëren. Is er een consensus over Stack Overflow?


Antwoord 1, autoriteit 100%

Ik zie de noodzaak niet echt, omdat een module met functies (en geen klasse) goed zou dienen als singleton. Al zijn variabelen zouden gebonden zijn aan de module, die toch niet herhaaldelijk kon worden geïnstantieerd.

Als je een klasse wilt gebruiken, is er geen manier om privéklassen of privéconstructors in Python te maken, dus je kunt je niet beschermen tegen meerdere instanties, behalve alleen via conventie in het gebruik van je API. Ik zou nog steeds gewoon methoden in een module plaatsen en de module als de singleton beschouwen.


Antwoord 2, autoriteit 83%

Hier is mijn eigen implementatie van singletons. Het enige wat je hoeft te doen is de klas versieren; om de singleton te krijgen, moet je de methode Instancegebruiken. Hier is een voorbeeld:

@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

En hier is de code:

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)

Antwoord 3, autoriteit 51%

U kunt de __new__methode als volgt overschrijven:

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"

Antwoord 4, autoriteit 31%

Een iets andere benadering om de singleton in Python te implementeren is het borgpatroonvan Alex Martelli (Google-medewerker en Python-genie).

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

Dus in plaats van alle instanties te dwingen dezelfde identiteit te hebben, delen ze de status.


Antwoord 5, autoriteit 22%

De module-aanpak werkt goed. Als ik absoluut een eenling nodig heb, geef ik de voorkeur aan de Metaclass-aanpak.

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

Antwoord 6, autoriteit 12%

Bekijk deze implementatie van PEP318, waarbij het singleton-patroon wordt geïmplementeerd met een decorateur:

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

Antwoord 7, autoriteit 8%

De Python-documentatiedekt dit wel:

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

Ik zou het waarschijnlijk herschrijven om er zo uit te zien:

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

Het zou relatief schoon moeten zijn om dit uit te breiden:

class Bus(Singleton):
    def init(self, label=None, *args, **kwds):
        self.label = label
        self.channels = [Channel("system"), Channel("app")]
        ...

Antwoord 8, autoriteit 7%

Zoals het geaccepteerde antwoordzegt, is de meest idiomatische manier om gewoon een module te gebruiken.

Met dat in gedachten, hier is een proof of concept:

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

Zie het Python-gegevensmodelvoor meer informatie over __new__.

Voorbeeld:

@singleton
class Duck(object):
    pass
if Duck() is Duck():
    print "It works!"
else:
    print "It doesn't work!"

Opmerkingen:

  1. Hiervoor moet je klassen nieuwe stijl gebruiken (afgeleid van object).

  2. De singleton wordt geïnitialiseerd wanneer deze wordt gedefinieerd, in plaats van de eerste keer dat deze wordt gebruikt.

  3. Dit is slechts een speelgoedvoorbeeld. Ik heb dit eigenlijk nooit in productiecode gebruikt en ben dat ook niet van plan.


Antwoord 9, autoriteit 6%

Ik ben hier erg onzeker over, maar mijn project gebruikt ‘convention singletons’ (geen afgedwongen singletons), dat wil zeggen, als ik een klasse heb met de naam DataController, definieer ik dit in dezelfde module :

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

Het is niet elegant, want het zijn zes volledige regels. Maar al mijn singletons gebruiken dit patroon, en het is op zijn minst erg expliciet (wat pythonisch is).


Antwoord 10, autoriteit 4%

De ene keer dat ik een singleton in Python schreef, gebruikte ik een klasse waarin alle lidfuncties de classmethod-decorator hadden.

class Foo:
    x = 1
    @classmethod
    def increment(cls, y=1):
        cls.x += y

Antwoord 11, autoriteit 3%

Het maken van een singleton-decorateur (ook wel een annotatie genoemd) is een elegante manier als je in de toekomst klassen wilt decoreren (annoteren). Dan zet je gewoon @singleton voor je klassendefinitie.

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

Antwoord 12, autoriteit 2%

Er zijn ook enkele interessante artikelen op de Google Testing-blog, waarin wordt besproken waarom singleton slecht is/kan zijn en een anti-patroon is:


Antwoord 13, autoriteit 2%

Ik denk dat het forcerenvan een klasse of instantie om een singleton te zijn, overdreven is. Persoonlijk definieer ik graag een normale instantieerbare klasse, een semi-private referentie en een eenvoudige fabrieksfunctie.

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

Of als er geen probleem is met instantiëren wanneer de module voor het eerst wordt geïmporteerd:

class NothingSpecial:
    pass
THE_ONE_AND_ONLY = NothingSpecial()

Op die manier kun je tests schrijven tegen nieuwe instanties zonder bijwerkingen, en het is niet nodig om de module te besprenkelen met globale statements, en indien nodig kun je in de toekomst varianten afleiden.


Antwoord 14

Het Singleton-patroon geïmplementeerd met Pythonmet dank aan ActiveState.

Het lijkt erop dat de truc is om de klasse die slechts één instantie zou moeten hebben, in een andere klasse te plaatsen.


Antwoord 15

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

Antwoord 16

Ok, eenling kan goed of slecht zijn, ik weet het. Dit is mijn implementatie, en ik breid gewoon een klassieke benadering uit om een cache binnenin te introduceren en veel instanties van een ander type of, veel instanties van hetzelfde type, maar met verschillende argumenten te produceren.

Ik noemde het Singleton_group, omdat het vergelijkbare instanties groepeert en voorkomt dat een object van dezelfde klasse, met dezelfde argumenten, kan worden gemaakt:

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

Elk object heeft de singleton-cache… Dit kan slecht zijn, maar voor sommigen werkt het geweldig 🙂


Antwoord 17

Mijn eenvoudige oplossing die is gebaseerd op de standaardwaarde van functieparameters.

def getSystemContext(contextObjList=[]):
    if len( contextObjList ) == 0:
        contextObjList.append( Context() )
        pass
    return contextObjList[0]
class Context(object):
    # Anything you want here

Antwoord 18

Omdat ik relatief nieuw ben in Python, weet ik niet zeker wat het meest voorkomende idioom is, maar het eenvoudigste wat ik kan bedenken is om gewoon een module te gebruiken in plaats van een klasse. Wat bijvoorbeeld instancemethoden in uw klasse zouden zijn, worden gewoon functies in de module en alle gegevens worden gewoon variabelen in de module in plaats van leden van de klasse. Ik vermoed dat dit de pythonische benadering is om het soort probleem op te lossen waarvoor mensen singletons gebruiken.

Als je echt een singleton-klasse wilt, is er een redelijke implementatie beschreven op de eerste hit op Googlevoor “Python singleton”, in het bijzonder:

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

Dat lijkt te werken.


Antwoord 19

Singletons halfbroer

Ik ben het volledig eens met staale en ik laat hier een voorbeeld achter van het creëren van een singleton halfbroer:

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

azal nu rapporteren als zijnde van dezelfde klasse als singleton, zelfs als het er niet zo uitziet. Dus singletons die ingewikkelde klassen gebruiken, zijn afhankelijk van het feit dat we niet veel met ze knoeien.

Als dat zo is, kunnen we hetzelfde effect hebben en eenvoudigere dingen gebruiken, zoals een variabele of een module. Maar als we klassen willen gebruiken voor de duidelijkheid en omdat in Python een klasse een object is, dus we hebben het object al (niet en instantie, maar het zal net zo doen).

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

Daar hebben we een mooie beweringsfout als we proberen een instantie te maken, en we kunnen statische leden van afleidingen opslaan en deze tijdens runtime wijzigen (ik ben dol op Python). Dit object is net zo goed als andere halfbroers (je kunt ze nog steeds maken als je wilt), maar het zal vanwege de eenvoud sneller werken.


Antwoord 20

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

Antwoord 21

In gevallen waarin u de bovenstaande op metaklassen gebaseerde oplossing niet wilt, en u niet van de eenvoudige op functie-decorator gebaseerde benadering houdt (bijvoorbeeld omdat in dat geval statische methoden op de singleton-klasse niet werken), is dit compromis werkt:

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)

Other episodes