Frage

Auf diese Frage gibt es hier bereits eine Antwort:

Es scheint viele Möglichkeiten zu geben, es zu definieren Singletons in Python.Gibt es eine einheitliche Meinung zu Stack Overflow?

War es hilfreich?

Lösung

Ich sehe keinen wirklichen Bedarf, da ein Modul mit Funktionen (und nicht einer Klasse) gut als Singleton dienen würde.Alle seine Variablen wären an das Modul gebunden, das ohnehin nicht wiederholt instanziiert werden könnte.

Wenn Sie eine Klasse verwenden möchten, gibt es in Python keine Möglichkeit, private Klassen oder private Konstruktoren zu erstellen. Daher können Sie sich nur durch Konventionen bei der Verwendung Ihrer API vor mehreren Instanziierungen schützen.Ich würde immer noch einfach Methoden in ein Modul einfügen und das Modul als Singleton betrachten.

Andere Tipps

Hier ist meine eigene Implementierung von Singletons.Alles was Sie tun müssen, ist die Klasse zu dekorieren;Um den Singleton zu erhalten, müssen Sie dann den verwenden Instance Methode.Hier ist ein Beispiel:

@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

Und hier ist der 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)

Sie können das überschreiben __new__ Methode wie diese:

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"

Ein etwas anderer Ansatz zur Implementierung des Singletons in Python ist der Borg-Muster von Alex Martelli (Google-Mitarbeiter und Python-Genie).

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

Anstatt also alle Instanzen dazu zu zwingen, die gleiche Identität zu haben, teilen sie sich den Status.

Der Modulansatz funktioniert gut.Wenn ich unbedingt einen Singleton brauche, bevorzuge ich den Metaclass-Ansatz.

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

Sehen Sie sich diese Implementierung von an PEP318, Implementierung des Singleton-Musters mit einem Dekorator:

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

@singleton
class MyClass:
    ...

Als die akzeptierte Antwort sagt, der idiomatischste Weg ist einfach ein Modul verwenden.

Vor diesem Hintergrund finden Sie hier einen 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

Siehe die Python-Datenmodell Weitere Informationen zu __new__.

Beispiel:

@singleton
class Duck(object):
    pass

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

Anmerkungen:

  1. Sie müssen Klassen neuen Stils verwenden (abgeleitet von object) dafür.

  2. Der Singleton wird bei seiner Definition initialisiert und nicht bei der ersten Verwendung.

  3. Dies ist nur ein Spielzeugbeispiel.Ich habe dies noch nie im Produktionscode verwendet und habe auch nicht vor, dies zu tun.

Ich bin mir da sehr unsicher, aber mein Projekt verwendet „Konventions-Singletons“ (keine erzwungenen Singletons), das heißt, wenn ich eine Klasse aufgerufen habe DataController, ich definiere dies im selben Modul:

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

Es ist nicht elegant, da es sich um volle sechs Zeilen handelt.Aber alle meine Singletons verwenden dieses Muster, und es ist zumindest sehr explizit (was pythonisch ist).

Der Python-Dokumentation deckt dies ab:

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

Ich würde es wahrscheinlich so umschreiben, dass es eher so aussieht:

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

Es sollte relativ sauber sein, dies zu erweitern:

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

Das eine Mal, als ich einen Singleton in Python geschrieben habe, habe ich eine Klasse verwendet, in der alle Mitgliedsfunktionen den Klassenmethoden-Dekorator hatten.

class foo:
  x = 1

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

Es gibt auch einige interessante Artikel im Google Testing-Blog, in denen erörtert wird, warum Singletons schlecht sind bzw. sein könnten und ein Anti-Pattern darstellen:

Das Erstellen eines Singleton-Dekorators (auch Annotation genannt) ist eine elegante Möglichkeit, wenn Sie in Zukunft Klassen dekorieren (annotieren) möchten.Dann setzen Sie einfach @singleton vor Ihre Klassendefinition.

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

@singleton
class MyClass:
    ...

Hier ist ein Beispiel aus Peter Norvigs Python IAQ Wie erstelle ich das Singleton-Muster in Python? (Sie sollten die Suchfunktion Ihres Browsers verwenden, um diese Frage zu finden. Es gibt leider keinen direkten Link.)

Auch Bruce Eckel hat in seinem Buch ein weiteres Beispiel Denken in Python (Auch hier gibt es keinen direkten Link zum Code)

ich denke, dass zwingen Eine Klasse oder Instanz als Singleton zu verwenden, ist übertrieben.Persönlich definiere ich gerne eine normale instanziierbare Klasse, eine halbprivate Referenz und eine einfache Factory-Funktion.

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

Oder wenn es beim ersten Import des Moduls kein Problem mit der Instanziierung gibt:

class NothingSpecial:
    pass

THE_ONE_AND_ONLY = NothingSpecial()

Auf diese Weise können Sie ohne Nebenwirkungen Tests für neue Instanzen schreiben und müssen das Modul nicht mit globalen Anweisungen überhäufen. Bei Bedarf können Sie in Zukunft Varianten ableiten.

Das mit Python implementierte Singleton-Muster Mit freundlicher Genehmigung von ActiveState.

Es sieht so aus, als ob der Trick darin besteht, die Klasse, die nur eine Instanz haben soll, in eine andere Klasse einzufügen.

Da ich relativ neu in Python bin, bin ich mir nicht sicher, was die gebräuchlichste Redewendung ist, aber das Einfachste, was mir einfällt, ist die Verwendung eines Moduls anstelle einer Klasse.Was Instanzmethoden Ihrer Klasse gewesen wären, werden im Modul zu einfachen Funktionen, und alle Daten werden im Modul einfach zu Variablen und nicht zu Mitgliedern der Klasse.Ich vermute, dass dies der pythonische Ansatz zur Lösung der Art von Problem ist, für die Menschen Singletons verwenden.

Wenn Sie wirklich eine Singleton-Klasse wollen, finden Sie hier eine sinnvolle Implementierung erster Treffer bei Google für „Python-Singleton“, insbesondere:

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

Das scheint den Zweck zu erfüllen.

Okay, Singleton könnte gut oder böse sein, ich weiß.Dies ist meine Implementierung, und ich erweitere einfach einen klassischen Ansatz, um einen Cache darin einzuführen und viele Instanzen eines anderen Typs oder viele Instanzen desselben Typs, aber mit unterschiedlichen Argumenten, zu erzeugen.

Ich habe es Singleton_group genannt, weil es ähnliche Instanzen gruppiert und verhindert, dass ein Objekt derselben Klasse mit denselben Argumenten erstellt werden kann:

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

Jedes Objekt trägt den Singleton-Cache ...Das könnte böse sein, aber bei manchen funktioniert es großartig :)

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

Meine einfache Lösung, die auf dem Standardwert von Funktionsparametern basiert.

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

class Context(object):
    # Anything you want here

Singletons Halbbruder

Ich stimme Staale voll und ganz zu und hinterlasse hier ein Beispiel für die Erstellung eines Singleton-Halbbruders:

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

a wird jetzt als zur gleichen Klasse wie Singleton gehörend gemeldet, auch wenn es nicht so aussieht.Singletons, die komplizierte Klassen verwenden, hängen also letztendlich davon ab, dass wir uns nicht viel mit ihnen anlegen.

Dadurch können wir den gleichen Effekt erzielen und einfachere Dinge wie eine Variable oder ein Modul verwenden.Dennoch, wenn wir Klassen aus Gründen der Klarheit und aus diesem Grund verwenden möchten In Python ist eine Klasse ein Objekt, wir haben also bereits das Objekt (nicht eine Instanz, aber es funktioniert genauso).

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

Dort haben wir einen netten Assertionsfehler, wenn wir versuchen, eine Instanz zu erstellen, und wir können statische Mitglieder in Ableitungen speichern und zur Laufzeit Änderungen daran vornehmen (ich liebe Python).Dieses Objekt ist genauso gut wie andere Halbbrüder (Sie können sie immer noch erstellen, wenn Sie möchten), es läuft jedoch aufgrund der Einfachheit tendenziell schneller.

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

In Fällen, in denen Sie die obige metaklassenbasierte Lösung nicht möchten und Ihnen der einfache, auf Funktionsdekoratoren basierende Ansatz nicht gefällt (z. B.Da in diesem Fall statische Methoden für die Singleton-Klasse nicht funktionieren), funktioniert dieser Kompromiss:

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)
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top