Erstellen eines Singletons in Python

945

Diese Frage dient nicht der Diskussion, ob das Singleton-Entwurfsmuster wünschenswert ist, ein Anti-Muster ist oder für irgendwelche Religionskriege, sondern um zu diskutieren, wie dieses Muster in Python am besten auf eine Weise implementiert werden kann, die am pythonischsten ist. In diesem Fall definiere ich "am pythonischsten" so, dass es dem "Prinzip des geringsten Erstaunens" folgt .

Ich habe mehrere Klassen, die zu Singletons werden würden (mein Anwendungsfall ist für einen Logger, aber das ist nicht wichtig). Ich möchte nicht mehrere Klassen mit zusätzlichem Kaugummi überladen, wenn ich einfach erben oder dekorieren kann.

Beste Methoden:


Methode 1: Ein Dekorateur

def singleton(class_):
    instances = {}
    def getinstance(*args, **kwargs):
        if class_ not in instances:
            instances[class_] = class_(*args, **kwargs)
        return instances[class_]
    return getinstance

@singleton
class MyClass(BaseClass):
    pass

Vorteile

  • Dekorateure sind auf eine Weise additiv, die oft intuitiver ist als Mehrfachvererbung.

Nachteile

  • Während mit MyClass () erstellte Objekte echte Singleton-Objekte sind, ist MyClass selbst eine Funktion, keine Klasse, sodass Sie keine Klassenmethoden daraus aufrufen können. Auch für m = MyClass(); n = MyClass(); o = type(n)();dannm == n && m != o && n != o

Methode 2: Eine Basisklasse

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

class MyClass(Singleton, BaseClass):
    pass

Vorteile

  • Es ist eine wahre Klasse

Nachteile

  • Mehrfachvererbung - eugh! __new__könnte während der Vererbung von einer zweiten Basisklasse überschrieben werden? Man muss mehr denken als nötig.

Methode 3: Eine Metaklasse

class Singleton(type):
    _instances = {}
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]

#Python2
class MyClass(BaseClass):
    __metaclass__ = Singleton

#Python3
class MyClass(BaseClass, metaclass=Singleton):
    pass

Vorteile

  • Es ist eine wahre Klasse
  • Deckt die Vererbung automatisch ab
  • Verwendet __metaclass__für den richtigen Zweck (und machte mich darauf aufmerksam)

Nachteile

  • Sind da irgendwelche?

Methode 4: Dekorateur, der eine gleichnamige Klasse zurückgibt

def singleton(class_):
    class class_w(class_):
        _instance = None
        def __new__(class_, *args, **kwargs):
            if class_w._instance is None:
                class_w._instance = super(class_w,
                                    class_).__new__(class_,
                                                    *args,
                                                    **kwargs)
                class_w._instance._sealed = False
            return class_w._instance
        def __init__(self, *args, **kwargs):
            if self._sealed:
                return
            super(class_w, self).__init__(*args, **kwargs)
            self._sealed = True
    class_w.__name__ = class_.__name__
    return class_w

@singleton
class MyClass(BaseClass):
    pass

Vorteile

  • Es ist eine wahre Klasse
  • Deckt die Vererbung automatisch ab

Nachteile

  • Gibt es keinen Aufwand für das Erstellen jeder neuen Klasse? Hier erstellen wir zwei Klassen für jede Klasse, die wir zu einem Singleton machen möchten. Obwohl dies in meinem Fall in Ordnung ist, mache ich mir Sorgen, dass dies möglicherweise nicht skaliert. Natürlich wird diskutiert, ob es zu einfach sein sollte, dieses Muster zu skalieren ...
  • Was ist der Sinn des _sealedAttributs
  • super()Gleichnamige Methoden können für Basisklassen nicht aufgerufen werden, da sie wiederkehren. Dies bedeutet, dass Sie __new__eine Klasse, für die Sie einen Aufruf benötigen, nicht anpassen und nicht in Unterklassen unterteilen können __init__.

Methode 5: ein Modul

eine Moduldatei singleton.py

Vorteile

  • Einfach ist besser als komplex

Nachteile

theheadofabroom
quelle
12
Weitere drei Techniken: Verwenden Sie stattdessen ein Modul (oft - im Allgemeinen denke ich - ist dies ein geeigneteres Muster für Python, aber es hängt ein wenig davon ab, was Sie damit machen); macht eine einzelne Instanz und mit ihr umgehen , anstatt ( foo.xoder , wenn Sie darauf bestehen , Foo.xstatt Foo().x); Verwenden Sie Klassenattribute und statische / Klassenmethoden ( Foo.x).
Chris Morgan
10
@ChrisMorgan: Wenn Sie nur Klassen- / statische Methoden verwenden möchten, machen Sie sich eigentlich keine Mühe, eine Klasse zu erstellen.
Cat Plus Plus
2
@Cat: Der Effekt ist ähnlich, aber die Gründe für das Erstellen einer globalen Variablen können so gut wie alles sein, einschließlich des Nichtwissens. Warum erstellt man einen Singleton? Wenn Sie fragen müssen, sollten Sie nicht hier sein. Diese Aussage ist nicht nur pythonischer, sondern vereinfacht auch die Wartung erheblich. Ja, Singletons sind syntaktischer Zucker für Globale, aber Klassen sind syntaktischer Zucker für eine ganze Reihe unschöner Dinge, und ich glaube, niemand wird Ihnen sagen, dass Sie ohne sie immer besser dran sind.
Theheadofabroom
14
@BiggAl: Singletons sind keine Pythonic, egal wie Sie sie implementieren. Sie sind bestenfalls ein Zeichen für ein fehlerhaftes Design.
Cat Plus Plus
8
Die Anti-Signletons-Stimmung ist die schlechteste Frachtkult-Programmierung. Gleiches gilt für Leute, die hören (nur wenige haben sich die Mühe gemacht, tatsächlich zu lesen) "Gehe zu Aussage, die als schädlich angesehen wird" und denken, Gotos seien ein Zeichen für schlechten Code, unabhängig vom Kontext.
Hejazzman

Antworten:

658

Verwenden Sie eine Metaklasse

Ich würde Methode 2 empfehlen , aber Sie sind besser dran, eine Metaklasse als eine Basisklasse zu verwenden. Hier ist eine Beispielimplementierung:

class Singleton(type):
    _instances = {}
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]

class Logger(object):
    __metaclass__ = Singleton

Oder in Python3

class Logger(metaclass=Singleton):
    pass

Wenn Sie __init__jedes Mal ausführen möchten, wenn die Klasse aufgerufen wird, fügen Sie hinzu

        else:
            cls._instances[cls].__init__(*args, **kwargs)

zur ifAussage in Singleton.__call__.

Ein paar Worte zu Metaklassen. Eine Metaklasse ist die Klasse einer Klasse ; Das heißt, eine Klasse ist eine Instanz ihrer Metaklasse . Sie finden die Metaklasse eines Objekts in Python mit type(obj). Normale Klassen neuen Stils sind vom Typ type. Loggerim obigen Code wird vom Typ sein class 'your_module.Singleton', genauso wie die (einzige) Instanz von Loggervom Typ sein wird class 'your_module.Logger'. Wenn Sie Logger rufen mit Logger(), fragt Python zuerst die Metaklasse von Logger, Singleton, was zu tun ist, so dass beispielsweise die Schaffung vorbelegt werden. Dieser Vorgang ist der gleiche wie bei Python, bei dem eine Klasse gefragt wird, was zu tun ist, indem sie aufruft, __getattr__wenn Sie dabei auf eines ihrer Attribute verweisen myclass.attribute.

Eine Metaklasse entscheidet im Wesentlichen, was die Definition einer Klasse bedeutet und wie diese Definition implementiert wird. Siehe zum Beispiel http://code.activestate.com/recipes/498149/ , das im Wesentlichen C-Stile structin Python mithilfe von Metaklassen neu erstellt. Der Thread Was sind einige (konkrete) Anwendungsfälle für Metaklassen? liefert auch einige Beispiele, sie scheinen im Allgemeinen mit deklarativer Programmierung in Zusammenhang zu stehen, insbesondere wie sie in ORMs verwendet werden.

Wenn Sie in dieser Situation Ihre Methode 2 verwenden und eine Unterklasse eine __new__Methode definiert , wird diese bei jedem Aufruf ausgeführt, SubClassOfSingleton()da sie für den Aufruf der Methode verantwortlich ist, die die gespeicherte Instanz zurückgibt. Bei einer Metaklasse wird sie nur einmal aufgerufen , wenn die einzige Instanz erstellt wird. Sie möchten anpassen, was es bedeutet, die Klasse aufzurufen , was durch ihren Typ bestimmt wird.

Im Allgemeinen ist es sinnvoll , eine Metaklasse zum Implementieren eines Singletons zu verwenden. Ein Singleton ist etwas Besonderes, da er nur einmal erstellt wird. Mit einer Metaklasse können Sie die Erstellung einer Klasse anpassen . Durch die Verwendung einer Metaklasse erhalten Sie mehr Kontrolle, falls Sie die Definitionen der Singleton-Klassen auf andere Weise anpassen müssen.

Ihre Singletons benötigen keine Mehrfachvererbung (da die Metaklasse keine Basisklasse ist). Für Unterklassen der erstellten Klasse , die Mehrfachvererbung verwenden, müssen Sie jedoch sicherstellen, dass die Singleton-Klasse die erste / ganz links stehende Metaklasse mit einer neu definierten Metaklasse ist __call__Es ist sehr unwahrscheinlich, dass dies ein Problem darstellt. Das Instanz-Diktat befindet sich nicht im Namespace der Instanz, sodass es nicht versehentlich überschrieben wird.

Sie werden auch hören, dass das Singleton-Muster gegen das "Prinzip der Einzelverantwortung" verstößt - jede Klasse sollte nur eines tun . Auf diese Weise müssen Sie sich nicht darum kümmern, eine Sache des Codes durcheinander zu bringen, wenn Sie eine andere ändern müssen, da sie getrennt und gekapselt sind. Die Metaklassenimplementierung besteht diesen Test . Die Metaklasse ist für die Durchsetzung des Musters verantwortlich , und die erstellten Klassen und Unterklassen müssen nicht wissen, dass es sich um Singletons handelt . Methode Nr. 1 besteht diesen Test nicht, wie Sie mit "MyClass selbst ist eine Funktion, keine Klasse, daher können Sie keine Klassenmethoden daraus aufrufen" festgestellt haben.

Python 2 und 3 kompatible Version

Um etwas zu schreiben, das sowohl in Python2 als auch in Python 3 funktioniert, muss ein etwas komplizierteres Schema verwendet werden. Da in der Regel metaclasses Subklassen von Typ sind type, dann ist es möglich , eine zu verwenden , um dynamisch eine Zwischenbasisklasse zur Laufzeit mit ihm als seine Metaklasse zu erstellen und verwenden Sie dann , dass als die Basisklasse der öffentlichen SingletonBasisklasse. Es ist schwieriger zu erklären als zu tun, wie im Folgenden dargestellt:

# works in Python 2 & 3
class _Singleton(type):
    """ A metaclass that creates a Singleton base class when called. """
    _instances = {}
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(_Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]

class Singleton(_Singleton('SingletonMeta', (object,), {})): pass

class Logger(Singleton):
    pass

Ein ironischer Aspekt dieses Ansatzes ist, dass zur Implementierung einer Metaklasse Unterklassen verwendet werden. Ein möglicher Vorteil ist, dass im Gegensatz zu einer reinen Metaklasse isinstance(inst, Singleton)zurückkehren wird True.

Korrekturen

Bei einem anderen Thema haben Sie dies wahrscheinlich bereits bemerkt, aber die Implementierung der Basisklasse in Ihrem ursprünglichen Beitrag ist falsch. _instancesmuss auf die Klasse verwiesen werden , muss verwendet werden super()oder Sie rekursieren und __new__ist eigentlich eine statische Methode, an die Sie die Klasse übergeben müssen , keine Klassenmethode, da die eigentliche Klasse noch nicht erstellt wurde, als sie erstellt wurde wird genannt. All diese Dinge gelten auch für eine Metaklassenimplementierung.

class Singleton(object):
  _instances = {}
  def __new__(class_, *args, **kwargs):
    if class_ not in class_._instances:
        class_._instances[class_] = super(Singleton, class_).__new__(class_, *args, **kwargs)
    return class_._instances[class_]

class MyClass(Singleton):
  pass

c = MyClass()

Dekorateur, der eine Klasse zurückgibt

Ich habe ursprünglich einen Kommentar geschrieben, aber er war zu lang, deshalb füge ich ihn hier hinzu. Methode 4 ist besser als die andere Decorator-Version, aber es ist mehr Code als für einen Singleton erforderlich, und es ist nicht so klar, was es tut.

Die Hauptprobleme ergeben sich daraus, dass die Klasse eine eigene Basisklasse ist. Ist es nicht seltsam, wenn eine Klasse eine Unterklasse einer nahezu identischen Klasse mit demselben Namen ist, die nur in ihrem __class__Attribut existiert? Das bedeutet auch , dass Sie nicht definieren können alle Methoden, die die Methode mit dem gleichen Namen auf ihrer Basisklasse aufrufen mit , super()weil sie Rekursion wird. Dies bedeutet, dass Ihre Klasse nicht angepasst werden __new__kann und nicht von Klassen abgeleitet werden kann, die sie aufrufen __init__müssen.

Wann wird das Singleton-Muster verwendet?

Ihr Anwendungsfall ist eines der besseren Beispiele für die Verwendung eines Singletons. Sie sagen in einem der Kommentare: "Für mich war das Protokollieren immer ein natürlicher Kandidat für Singletons." Du hast absolut recht .

Wenn Leute sagen, Singletons seien schlecht, ist der häufigste Grund, dass sie einen impliziten gemeinsamen Zustand haben . Während bei globalen Variablen und Modulimporten der obersten Ebene ein expliziter gemeinsamer Status vorliegt, werden andere Objekte, die weitergegeben werden, im Allgemeinen instanziiert. Dies ist mit zwei Ausnahmen ein guter Punkt .

Die erste und eine, die an verschiedenen Stellen erwähnt wird, ist, wenn die Singletons konstant sind . Die Verwendung globaler Konstanten, insbesondere von Aufzählungen, wird allgemein akzeptiert und als vernünftig angesehen, da keiner der Benutzer sie für einen anderen Benutzer durcheinander bringen kann . Dies gilt auch für einen konstanten Singleton.

Die zweite Ausnahme, die weniger erwähnt wird, ist das Gegenteil - wenn der Singleton nur eine Datensenke ist , keine Datenquelle (direkt oder indirekt). Aus diesem Grund fühlen sich Logger wie eine "natürliche" Verwendung für Singletons. Da die verschiedenen Benutzer die Logger nicht so ändern, wie es anderen Benutzern wichtig ist, gibt es keinen wirklich gemeinsam genutzten Status . Dies negiert das Hauptargument gegen das Singleton-Muster und macht sie aufgrund ihrer einfachen Verwendung für die Aufgabe zu einer vernünftigen Wahl .

Hier ist ein Zitat von http://googletesting.blogspot.com/2008/08/root-cause-of-singletons.html :

Nun gibt es eine Art von Singleton, die in Ordnung ist. Das ist ein Singleton, bei dem alle erreichbaren Objekte unveränderlich sind. Wenn alle Objekte unveränderlich sind, hat Singleton keinen globalen Status, da alles konstant ist. Aber es ist so einfach, diese Art von Singleton in einen veränderlichen zu verwandeln, es ist ein sehr rutschiger Hang. Deshalb bin ich auch gegen diese Singletons, nicht weil sie schlecht sind, sondern weil es für sie sehr leicht ist, schlecht zu werden. (Nebenbei bemerkt, Java-Enumeration ist genau diese Art von Singletons. Solange Sie keinen Status in Ihre Enumeration einfügen, sind Sie in Ordnung, also bitte nicht.)

Die andere Art von Singletons, die halbwegs akzeptabel sind, sind solche, die die Ausführung Ihres Codes nicht beeinflussen. Sie haben keine "Nebenwirkungen". Die Protokollierung ist ein perfektes Beispiel. Es ist mit Singletons und globalem Zustand geladen. Dies ist akzeptabel (da es Sie nicht verletzt), da sich Ihre Anwendung nicht anders verhält, unabhängig davon, ob ein bestimmter Logger aktiviert ist oder nicht. Die Informationen hier fließen in eine Richtung: Von Ihrer Anwendung in den Logger. Auch wenn Logger als global eingestuft werden, da keine Informationen von Loggern in Ihre Anwendung fließen, sind Logger akzeptabel. Sie sollten Ihren Logger trotzdem injizieren, wenn Sie möchten, dass Ihr Test bestätigt, dass etwas protokolliert wird, aber im Allgemeinen sind Logger trotz ihres Status nicht schädlich.

agf
quelle
5
Nein, Singletons sind niemals gut. Die Protokollierung ist möglicherweise ein guter Kandidat für ein globales Unternehmen (so schrecklich sie auch sind), aber sicherlich kein Singleton.
Cat Plus Plus
11
Schauen Sie sich googletesting.blogspot.com/2008/08/… an . Es ist im Allgemeinen Anti-Singleton (aus gutem Grund), aber es hat eine gute Erklärung dafür, warum unveränderliche Singletons und Singletons ohne Nebenwirkungen nicht die gleichen Probleme haben, wenn Sie vorsichtig sind. Ich werde es am Ende meines Beitrags ein wenig zitieren.
agf
4
Mein Problem mit Singletons ist die dumme Prämisse von "nur einer Instanz". Das und eine Tonne Fadensicherheitsprobleme. Und Abhängigkeit versteckt. Globals sind schlecht und Singletons sind nur Globals mit mehr Problemen.
Cat Plus Plus
4
@Cat Es gibt sehr gute Verwendungsmöglichkeiten für Singletons. Eine davon ist die verzögerte Instanziierung von Hardwaremodulen (insbesondere in Single-Threaded-Anwendungen) (es gibt jedoch auch thread-sichere Singletons).
Paul Manta
3
@Alcott __new__in einer Metaklasse ist, wenn die Klasse neu ist - wenn sie definiert ist, nicht wenn die Instanz neu wäre. Das Aufrufen der class ( MyClass()) ist die Operation, die Sie überschreiben möchten, nicht die Definition der Klasse. Wenn Sie wirklich verstehen möchten, wie Python funktioniert, können Sie am besten docs.python.org/reference/datamodel.html lesen (außer es weiterhin zu verwenden) . Eine gute Referenz für Metaklassen ist eli.thegreenplace.net/2011/08/14/python-metaclasses-by-example . Ein guter Artikel über Singletons ist die Serie aus dem Google-Blog, die ich in dieser Antwort verlinkt habe.
agf
91
class Foo(object):
     pass

some_global_variable = Foo()

Module werden nur einmal importiert, alles andere überdenkt. Verwenden Sie keine Singletons und versuchen Sie, keine Globals zu verwenden.

Cat Plus Plus
quelle
14
Warum hast du gesagt "Benutze keine Singletons"? Irgendein Grund?
Alcott
3
Dies funktioniert nicht, wenn der Singleton eingelegt werden muss. Mit dem Beispiel, das Sie gegeben haben:s = some_global_variable; str = pickle.dumps(s); s1 = pickle.loads(str); print s is s1; # False
Dividebyzero
4
@dividebyzero: Der isOperator testet auf Zeigergleichheit . Ich wäre ziemlich überrascht - bis zu dem Punkt, dass ich es als Fehler bezeichne -, wenn ich pickle.loadseinen Verweis auf ein bereits vorhandenes Objekt anstelle eines Verweises auf ein neu erstelltes Objekt zurückgeben würde. Testen Sie daher, ob s is s1Sie nichts über die Eignung der Verwendung von Modulen als Singletons wissen.
Jonas Kölker
1
@ JonasKölker pickle.loads()macht das schon, zB für Instanzen von boolund NoneType. pickle.loads(pickle.dumps(False)) is FalseErträgeTrue
Dan Passaro
2
@ Leo-the-Manic: fairer Punkt; aber das ist nur ein Nebeneffekt von Python der Objekte Internierung True, Falseund None, und hat nichts mit dem Code - behind zu tun pickle.loads. Es ist auch sicher, nur für schreibgeschützte Objekte zu tun. Wenn Sie pickle.loadseinen Verweis auf ein bereits vorhandenes modifizierbares Objekt wie ein Modul zurückgeben würden, wäre dies ein Fehler. (Und so stehe ich zu meiner Implikation, dass das Codebeispiel von dividbyzero nichts beweist.)
Jonas Kölker
69

Verwenden Sie ein Modul. Es wird nur einmal importiert. Definieren Sie einige globale Variablen darin - sie sind Singletons 'Attribute'. Fügen Sie einige Funktionen hinzu - die 'Methoden' des Singletons.

warvariuc
quelle
11
Am Ende haben Sie also ... keine Klasse. Sie können es nicht als Klasse verwenden, Sie können keine anderen Klassen darauf aufbauen, Sie verwenden die
Importsyntax
16
Wenn Sie andere Klassen darauf aufbauen können, handelt es sich möglicherweise nicht um einen Singleton. Sie könnten eine der abgeleiteten Klassen erstellen, aber auch eine der Basisklassen, aber die abgeleitete Klasse ist auch ein Mitglied der Basis, und Sie haben zwei der Basen, welche sollten Sie verwenden?
SingleNegationElimination
Dies funktioniert nicht modulübergreifend. In meinem "Haupt" -Modul habe ich einen Wert eingestellt. Ich verweise dann in einem anderen Modul und es ist null. Seufzer.
Paul Kenjora
1
@PaulKenjora Sie müssen einen Fehler in Ihrem Code haben. Wenn Sie eine globale Variable in einem Modul definieren und diese von einem anderen Modul aus aufrufen, sollte sie den Wert haben.
Warvariuc
Es hat den Wert, aber wenn ich ihn ändere, bleibt es nicht erhalten. Am Ende habe ich eine Klasse mit Eigenschaften in einem Modul verwendet, das funktioniert. Einfache Typen wie Globals haben bei mir nicht funktioniert (sie haben Werte verloren, sobald sich der Umfang geändert hat).
Paul Kenjora
29

Sie brauchen wahrscheinlich nie einen Singleton in Python. Definieren Sie einfach alle Ihre Daten und Funktionen in einem Modul und Sie haben einen De-facto-Singleton.

Wenn Sie wirklich unbedingt eine Singleton-Klasse haben müssen, würde ich gehen mit:

class My_Singleton(object):
    def foo(self):
        pass

my_singleton = My_Singleton()

Benutzen:

from mysingleton import my_singleton
my_singleton.foo()

Dabei ist mysingleton.py Ihr Dateiname, in dem My_Singleton definiert ist. Dies funktioniert, da Python den Code nach dem ersten Import einer Datei nicht erneut ausführt.

Alan Dyke
quelle
4
Meistens wahr, aber manchmal reicht das nicht aus. ZB habe ich ein Projekt mit der Notwendigkeit, Instanziierungen vieler Klassen auf DEBUG-Ebene zu protokollieren. Ich benötige Befehlszeilenoptionen, die beim Start analysiert werden, um die benutzerdefinierte Protokollierungsstufe festzulegen, bevor diese Klassen instanziiert werden. Instanziierungen auf Modulebene machen dies problematisch. Es ist möglich, dass ich die App sorgfältig strukturieren kann, damit alle diese Klassen erst nach Abschluss der CLI-Verarbeitung importiert werden. Die natürliche Struktur meiner App ist jedoch wichtiger als die dogmatische Einhaltung von "Singletons sind schlecht", da dies möglich ist ganz sauber gemacht.
CryingCyclops
Wenn Sie Ihren Code beim Patchen von my_singleton testen würden, wäre das möglich? da dieses my_singleton in einem anderen Modul instanziiert werden könnte.
Naveen
@Naveen - my_singleton ist ein einzelnes Objekt. Wenn Sie es "patchen", wirkt sich diese Änderung auf alle zukünftigen Referenzen aus, auch in anderen Modulen.
Alan Dyke
16

Hier ist ein Einzeiler für Sie:

singleton = lambda c: c()

So verwenden Sie es:

@singleton
class wat(object):
    def __init__(self): self.x = 1
    def get_x(self): return self.x

assert wat.get_x() == 1

Ihr Objekt wird eifrig instanziiert. Dies kann sein oder nicht, was Sie wollen.

Jonas Kölker
quelle
5
-1. Das ist sehr schlecht und albern. Sie definieren keine Klasse, die als Objekt verwendet werden soll. Sie können nicht mehr auf die Klasse zugreifen, ohne sehr hässlich zu sein wie type(wat)oder wat.__class__. Wenn Sie dies wirklich erreichen möchten, definieren Sie die Klasse besser und instanziieren Sie sie sofort, ohne sich mit dem Dekorateur herumschlagen zu müssen.
0xc0de
2
Warum müssen Sie wissen, wie man die Klasse eines Singletons verwendet? Verwenden Sie einfach das Singleton-Objekt.
Tolli
1
Es ist kein Singleton-Muster , daher sollte die Funktion IMO anders benannt werden.
GingerPlusPlus
8
Wikipedia: "Das Singleton-Muster ist ein Entwurfsmuster, das die Instanziierung einer Klasse auf ein Objekt beschränkt." Ich würde sagen, dass meine Lösung genau das tut. Okay, ich denke, man könnte es tun wat2 = type(wat)(), aber das ist Python, wir sind alle einverstanden mit Erwachsenen und all dem. Sie können nicht garantieren, dass es nur eine Instanz gibt, aber Sie können garantieren, dass Menschen, die eine zweite erstellen, hässlich aussehen und - wenn sie anständige, aufrechte Menschen sind - wie ein Warnzeichen für sie aussehen. Was vermisse ich?
Jonas Kölker
7

Check out Stapelüberlauf Frage Gibt es eine einfache, elegante Möglichkeit, Singletons in Python zu definieren? mit mehreren Lösungen.

Ich würde dringend empfehlen, Alex Martellis Vorträge über Designmuster in Python zu sehen: Teil 1 und Teil 2 . Insbesondere spricht er in Teil 1 über Singletons / Shared State Objects.

Anton
quelle
2
Dies ist zwar keine wirkliche Antwort auf meine Frage, aber die Ressourcen, auf die Sie verweisen, sind sehr nützlich. Ich gebe Ihnen widerwillig eine +1
theheadofabroom
3

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 die InstanceMethode verwenden. 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. 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.

    Limitations: The decorated class cannot be inherited from.

    """

    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)
Paul Manta
quelle
2
Es ist kein wahrer Singleton. SingletonList = Singleton(list).Instance(); print(SingletonList is type(SingletonList)())sollte Truein echtem Singleton drucken ; mit Ihren Code drucktFalse
GingerPlusPlus
@GingerPlusPlus Mir waren einige Einschränkungen bekannt, aber nicht die, auf die Sie hingewiesen haben. Danke, dass du es erwähnt hast. Leider habe ich im Moment keine Zeit, über eine Lösung für dieses Problem nachzudenken.
Paul Manta
2

Methode 3 scheint sehr ordentlich zu sein, aber wenn Sie möchten, dass Ihr Programm sowohl in Python 2 als auch in Python 3 ausgeführt wird , funktioniert es nicht. Selbst der Schutz der einzelnen Varianten mit Tests für die Python-Version schlägt fehl, da die Python 3-Version in Python 2 einen Syntaxfehler verursacht.

Vielen Dank an Mike Watkins: http://mikewatkins.ca/2008/11/29/python-2-and-3-metaclasses/ . Wenn Sie möchten, dass das Programm sowohl in Python 2 als auch in Python 3 funktioniert, müssen Sie Folgendes tun:

class Singleton(type):
    _instances = {}
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]

MC = Singleton('MC', (object), {})

class MyClass(MC):
    pass    # Code for the class implementation

Ich gehe davon aus, dass 'Objekt' in der Zuweisung durch die 'Basisklasse' ersetzt werden muss, aber ich habe das nicht versucht (ich habe Code wie abgebildet ausprobiert).

Tim
quelle
Dies ist sicherlich keine Metaklasse - in Python3 verwenden Sie eine Metaklasse, um MyClass zu erstellen, die Sie tun würdenclass MyClass(metaclass=Singleton)
theheadofabroom
Der Link mikewatkins.ca ist (effektiv) unterbrochen.
Peter Mortensen
1

Nun, abgesehen davon, dass Sie dem allgemeinen pythonischen Vorschlag zustimmen, eine globale Modulebene zu haben, wie wäre es damit:

def singleton(class_):
    class class_w(class_):
        _instance = None
        def __new__(class2, *args, **kwargs):
            if class_w._instance is None:
                class_w._instance = super(class_w, class2).__new__(class2, *args, **kwargs)
                class_w._instance._sealed = False
            return class_w._instance
        def __init__(self, *args, **kwargs):
            if self._sealed:
                return
            super(class_w, self).__init__(*args, **kwargs)
            self._sealed = True
    class_w.__name__ = class_.__name__
    return class_w

@singleton
class MyClass(object):
    def __init__(self, text):
        print text
    @classmethod
    def name(class_):
        print class_.__name__

x = MyClass(111)
x.name()
y = MyClass(222)
print id(x) == id(y)

Ausgabe ist:

111     # the __init__ is called only on the 1st time
MyClass # the __name__ is preserved
True    # this is actually the same instance
Bewachen
quelle
Was ist der Sinn des _sealedAttributs? Soweit ich sehe, macht das nichts? Etwas nervt mich daran, dass es nicht gut funktionieren sollte ... Ich werde später in dieser Woche einige Vergleichstests durchführen.
Theheadofabroom
_sealed stellt sicher, dass Ihr Init nur einmal ausgeführt wird. Ich sehe keinen Grund, warum es schlechter abschneiden sollte als der reguläre funktionsähnliche Dekorateur - die Funktion wird nur einmal pro Klasse ausgeführt und gibt eine neue geerbte Klasse zurück
Guard
Übrigens enthält Ihre Bearbeitung Registerkarten, die die Einrückungen aufbrechen. Sie sagen auch "Wir erstellen 2 Klassen" - meinen Sie damit, dass wir "1 zusätzliche Klasse" erstellen?
Wache
Ja, eine zusätzliche Klasse habe ich gemeint. Ich beabsichtige, Dinge aufzunehmen __init__, die bei jeder Initialisierung aufgerufen werden sollen. Nur ein einfaches 'In class.method initialisiert werden'.
Was die Einrückung betrifft
re init : Natürlich liegt es an Ihnen, ich habe nur versucht, das Singleton-Verhalten in anderen Sprachen nachzuahmen, wobei Konstruktorcode (der nicht genau init ist , aber in seinem Sinne sehr nahe) nur einmal aufgerufen wird, wenn Sie möchten, dass der init ist Jedes Mal aufgerufen, töte einfach alle Verweise auf versiegelte Leerzeichen / Tabulatoren - nun, dann müssen meine Emacs repariert werden. Wie auch immer, oben ist die korrigierte Version
Guard
1

Wie wäre es damit:

def singleton(cls):
    instance=cls()
    cls.__new__ = cls.__call__= lambda cls: instance
    cls.__init__ = lambda self: None
    return instance

Verwenden Sie es als Dekorateur für eine Klasse, die ein Singleton sein sollte. So was:

@singleton
class MySingleton:
    #....

Dies ähnelt dem singleton = lambda c: c()Dekorateur in einer anderen Antwort. Wie die andere Lösung hat die einzige Instanz den Namen der Klasse ( MySingleton). Mit dieser Lösung können Sie jedoch weiterhin Instanzen aus der Klasse "erstellen" (tatsächlich die einzige Instanz erhalten), indem Sie dies tun MySingleton(). Es verhindert auch, dass Sie zusätzliche Instanzen erstellen, indem Sie dies tun type(MySingleton)()( wodurch auch dieselbe Instanz zurückgegeben wird).

Tolli
quelle
Sie definieren keine Klasse, um sie als Objekt zu verwenden.
0xc0de
1
Jedes Mal type(MySingleton)(), wenn Sie anrufen , MySingleton.__init__()wird es aufgerufen und das Objekt wird mehrmals initialisiert. Sie können es schriftlich cls.__init__ = lambda self: passin Ihrem beheben singleton. Außerdem cls.__call__scheint das Überschreiben sinnlos und sogar schädlich zu sein - __call__in diesem Zusammenhang wird es verwendet, wenn Sie anrufen MySingleton(any, list, of, arguments), nicht wenn Sie anrufen type(MySingleton)(any, list, of, arguments).
GingerPlusPlus
@GingerPlusPlus, Danke, dass du darauf hingewiesen hast, dass dies beim __init__()Aufruf erneut aufgerufen wird type(MySingleton)(). Die von Ihnen vorgeschlagene Lösung (Hinzufügen cls.__init__ = lambda self: pass) führt zu einem Syntaxfehler, da der letzte Teil des Lambda-Ausdrucks ein Ausdruck und keine Anweisung sein muss. Das Hinzufügen cls.__init__ = lambda self: Nonefunktioniert jedoch, also habe ich das zu meiner Antwort hinzugefügt.
Tolli
1
@GingerPlusPlus, In Bezug auf die Verwendung von __call__. Meine Absicht war es, beides zu machen type(MySingleton)()und MySingleton()die Instanz zurückzugeben. Also macht es was ich wollte. Sie können sich MySingleton entweder als Typ des Singletons oder als Instanz des Singletons (oder als beides) vorstellen.
Tolli
1

Ich werfe meine in den Ring. Es ist ein einfacher Dekorateur.

from abc import ABC

def singleton(real_cls):

    class SingletonFactory(ABC):

        instance = None

        def __new__(cls, *args, **kwargs):
            if not cls.instance:
                cls.instance = real_cls(*args, **kwargs)
            return cls.instance

    SingletonFactory.register(real_cls)
    return SingletonFactory

# Usage
@singleton
class YourClass:
    ...  # Your normal implementation, no special requirements.

Vorteile Ich denke, es hat gegenüber einigen anderen Lösungen:

  • Es ist klar und prägnant (für mein Auge; D).
  • Seine Wirkung ist vollständig eingekapselt. Sie müssen nichts an der Implementierung von ändern YourClass. Dies beinhaltet, dass Sie keine Metaklasse für Ihre Klasse verwenden müssen (beachten Sie, dass sich die oben genannte Metaklasse im Werk befindet und nicht die "echte" Klasse).
  • Es ist nicht darauf angewiesen, irgendetwas mit Affen zu flicken.
  • Es ist für Anrufer transparent:
    • Anrufer importieren immer noch einfach YourClass, es sieht aus wie eine Klasse (weil es so ist) und sie verwenden es normal. Anrufer müssen nicht an eine Werksfunktion angepasst werden.
    • Was YourClass()instanziiert, ist immer noch eine echte Instanz der von YourClassIhnen implementierten, kein Proxy jeglicher Art, also keine Chance auf daraus resultierende Nebenwirkungen.
    • isinstance(instance, YourClass) und ähnliche Operationen funktionieren immer noch wie erwartet (obwohl dieses Bit abc erfordert, schließt Python <2.6 aus).

Ein Nachteil fällt mir ein: Klassenmethoden und statische Methoden der realen Klasse können nicht transparent über die Factory-Klasse aufgerufen werden, die sie versteckt. Ich habe dies selten genug verwendet, damit ich nie auf dieses Bedürfnis gestoßen bin, aber es könnte leicht behoben werden, indem eine benutzerdefinierte Metaklasse in der Factory verwendet wird, die implementiert, __getattr__()um den Zugriff auf all-ish-Attribute an die reale Klasse zu delegieren.

Ein verwandtes Muster, das ich tatsächlich als nützlicher empfunden habe (nicht, dass ich sage, dass solche Dinge überhaupt sehr oft erforderlich sind), ist ein "einzigartiges" Muster, bei dem das Instanziieren der Klasse mit denselben Argumenten dazu führt, dass dieselbe Instanz zurückgegeben wird. Dh ein "Singleton pro Argument". Das Obige passt sich diesem Brunnen an und wird noch prägnanter:

def unique(real_cls):

    class UniqueFactory(ABC):

        @functools.lru_cache(None)  # Handy for 3.2+, but use any memoization decorator you like
        def __new__(cls, *args, **kwargs):
            return real_cls(*args, **kwargs)

    UniqueFactory.register(real_cls)
    return UniqueFactory

Trotzdem stimme ich dem allgemeinen Rat zu, dass Sie, wenn Sie glauben, eines dieser Dinge zu brauchen, wahrscheinlich einen Moment innehalten und sich fragen sollten, ob Sie es wirklich tun. 99% der Zeit, YAGNI.

CryingCyclops
quelle
1
  • Wenn man mehrere Instanzen derselben Klasse haben möchte, aber nur, wenn die Argumente oder kwargs unterschiedlich sind, kann man dies verwenden
  • Ex.
    1. Wenn Sie über eine serialKommunikation für die Klassenverarbeitung verfügen und eine Instanz erstellen möchten, die Sie als Argument an die serielle Schnittstelle senden möchten, funktioniert dies mit dem herkömmlichen Ansatz nicht
    2. Mit den oben genannten Dekoratoren kann man mehrere Instanzen der Klasse erstellen, wenn die Argumente unterschiedlich sind.
    3. Für dieselben Argumente gibt der Dekorateur dieselbe Instanz zurück, die bereits erstellt wurde.
>>> from decorators import singleton
>>>
>>> @singleton
... class A:
...     def __init__(self, *args, **kwargs):
...         pass
...
>>>
>>> a = A(name='Siddhesh')
>>> b = A(name='Siddhesh', lname='Sathe')
>>> c = A(name='Siddhesh', lname='Sathe')
>>> a is b  # has to be different
False
>>> b is c  # has to be same
True
>>>
Siddhesh Suhas Sathe
quelle
0

Code basierend auf Tollis Antwort .

#decorator, modyfies new_cls
def _singleton(new_cls):
    instance = new_cls()                                              #2
    def new(cls):
        if isinstance(instance, cls):                                 #4
            return instance
        else:
            raise TypeError("I can only return instance of {}, caller wanted {}".format(new_cls, cls))
    new_cls.__new__  = new                                            #3
    new_cls.__init__ = lambda self: None                              #5
    return new_cls


#decorator, creates new class
def singleton(cls):
    new_cls = type('singleton({})'.format(cls.__name__), (cls,), {} ) #1
    return _singleton(new_cls)


#metaclass
def meta_singleton(name, bases, attrs):
    new_cls = type(name, bases, attrs)                                #1
    return _singleton(new_cls)

Erläuterung:

  1. Erstellen Sie eine neue Klasse, die von einer bestimmten erbt cls
    (sie ändert sich nicht, clsfalls jemand dies wünscht singleton(list)).

  2. Instanz erstellen. Vor dem Überschreiben ist __new__es so einfach.

  3. Wenn wir jetzt einfach eine Instanz erstellt haben, werden diese __new__mit der vor kurzem definierten Methode überschrieben .
  4. Die Funktion wird instancenur zurückgegeben, wenn dies vom Aufrufer erwartet wird, andernfalls wird sie ausgelöst TypeError.
    Die Bedingung ist nicht erfüllt, wenn jemand versucht, von einer dekorierten Klasse zu erben.

  5. Wenn __new__()eine Instanz von zurückgegeben wird cls, wird die __init__()Methode der neuen Instanz wie folgt aufgerufen__init__(self[, ...]) , wobei self die neue Instanz ist und die verbleibenden Argumente dieselben sind, an die übergeben wurde __new__().

    instanceist bereits initialisiert, daher wird die Funktion durch die Funktion ersetzt __init__, die nichts tut.

Sehen Sie, wie es online funktioniert

GingerPlusPlus
quelle
0

Es ist der Antwort von fab etwas ähnlich, aber nicht genau das gleiche.

Der Singleton-Vertrag erfordert nicht, dass wir den Konstruktor mehrmals aufrufen können. Da ein Singleton nur einmal erstellt werden sollte, sollte er nicht nur einmal erstellt werden? Das "Spoofing" des Konstrukteurs beeinträchtigt wohl die Lesbarkeit.

Mein Vorschlag ist also genau das:

class Elvis():
    def __init__(self):
        if hasattr(self.__class__, 'instance'):
            raise Exception()
        self.__class__.instance = self
        # initialisation code...

    @staticmethod
    def the():
        if hasattr(Elvis, 'instance'):
            return Elvis.instance
        return Elvis()

Dies schließt die Verwendung des Konstruktors oder des Feldes instancedurch den Benutzercode nicht aus:

if Elvis() is King.instance:

... wenn Sie sicher wissen, dass Elvisdas noch nicht erstellt wurde, und das Kinghat.

Aber es ermutigt Benutzer, die theMethode universell anzuwenden :

Elvis.the().leave(Building.the())

Um dies zu vervollständigen, können Sie auch überschreiben __delattr__(), um eine Ausnahme auszulösen, wenn versucht wird, eine Ausnahme zu löschen instance, und überschreiben __del__(), damit eine Ausnahme ausgelöst wird (es sei denn, wir wissen, dass das Programm endet ...).

Weitere Verbesserungen


Mein Dank geht an diejenigen, die mit Kommentaren und Änderungen geholfen haben, von denen weitere willkommen sind. Während ich Jython verwende, sollte dies allgemeiner funktionieren und threadsicher sein.

try:
    # This is jython-specific
    from synchronize import make_synchronized
except ImportError:
    # This should work across different python implementations
    def make_synchronized(func):
        import threading
        func.__lock__ = threading.Lock()

        def synced_func(*args, **kws):
            with func.__lock__:
                return func(*args, **kws)

        return synced_func

class Elvis(object): # NB must be subclass of object to use __new__
    instance = None

    @classmethod
    @make_synchronized
    def __new__(cls, *args, **kwargs):
        if cls.instance is not None:
            raise Exception()
        cls.instance = object.__new__(cls, *args, **kwargs)
        return cls.instance

    def __init__(self):
        pass
        # initialisation code...

    @classmethod
    @make_synchronized
    def the(cls):
        if cls.instance is not None:
            return cls.instance
        return cls()

Anmerkungen:

  1. Wenn Sie in python2.x keine Unterklasse von Objekten erstellen, erhalten Sie eine Klasse im alten Stil, die nicht verwendet wird __new__
  2. Beim Dekorieren __new__müssen Sie mit @classmethod dekorieren oder __new__es handelt sich um eine ungebundene Instanzmethode
  3. Dies könnte möglicherweise durch die Verwendung einer Metaklasse verbessert werden, da Sie so theeine Eigenschaft auf Klassenebene erstellen und möglicherweise in umbenennen könneninstance
Mike Nagetier
quelle
Obwohl dies eine etwas andere Interpretation des Singleton-Musters ist, bin ich mir ziemlich sicher, dass es immer noch gültig ist, obwohl ich möglicherweise versucht bin, __new__ anstelle von __ zu verwenden __init__, da es nur auf Klassenattribute einwirkt und dies verhindert, dass es kurzzeitig eine zweite Instanz gibt. Der Unterschied zwischen dieser und Methode 2 besteht darin, ob der Versuch, mehr als einmal zu initialisieren, die einzelne Instanz zurückgibt oder eine Ausnahme auslöst. Ich denke, ich bin froh, dass entweder das Singleton-Muster erfüllt ist, eines einfacher zu verwenden ist, während das andere expliziter ist, dass es ein Singleton ist.
Theheadofabroom
Offensichtlich verhindert die Verwendung des Klassennamens in __init__Unterklassen, aber während dies die Dinge einfacher macht, ist es nicht erforderlich
theheadofabroom
Danke ... ah ja, eine kurze zweite Instanz, bevor die Ausnahme ausgelöst wird. Ich habe das __init__so modifiziert , dass es hoffentlich unterklassifizierbar sein sollte ...
Mike Nagetier
Cool, thekönnte wahrscheinlich aus ähnlichen Gründen von einer Klassenmethode profitieren
theheadofabroom
ja, du hast Recht. Dann können Sie einen SuperElvis-Unterklassen-Singleton und (zum Beispiel) einen ImaginaryElvis-Unterklassen-Singleton haben ... und sie können nebeneinander existieren. Siehe zusätzliche Gedanken. Bitte zögern Sie nicht, meinen Code zu verbessern.
Mike Nagetier
0

Ein Liner (ich bin nicht stolz, aber er macht den Job):

class Myclass:
  def __init__(self):
      # do your stuff
      globals()[type(self).__name__] = lambda: self # singletonify
polvoazul
quelle
Dies kann die Arbeit erledigen, solange Ihre Klasse nicht in andere Module importiert wurde ...
Aran-Fey
Wahr. Wenn Sie die Kosten für die Initiierung der Klasse bezahlen können, können Sie unmittelbar nach der Klassendefinition eine Myclass () ausführen, um dies zu vermeiden.
Polvoazul
0

Wenn Sie keine verzögerte Initialisierung der Singleton-Instanz benötigen, sollte Folgendes einfach und threadsicher sein:

class A:
    instance = None
    # Methods and variables of the class/object A follow
A.instance = A()

Auf diese Weise Awird ein Singleton beim Modulimport initialisiert.

Serge Rogatch
quelle
0

Vielleicht verstehe ich das Singleton-Muster falsch, aber meine Lösung ist einfach und pragmatisch (pythonisch?). Dieser Code erfüllt zwei Ziele

  1. Machen Sie die Instanz von Fooüberall zugänglich (global).
  2. Es kann nur eine Instanz von Fooexistieren.

Dies ist der Code.

#!/usr/bin/env python3

class Foo:
    me = None

    def __init__(self):
        if Foo.me != None:
            raise Exception('Instance of Foo still exists!')

        Foo.me = self


if __name__ == '__main__':
    Foo()
    Foo()

Ausgabe

Traceback (most recent call last):
  File "./x.py", line 15, in <module>
    Foo()
  File "./x.py", line 8, in __init__
    raise Exception('Instance of Foo still exists!')
Exception: Instance of Foo still exists!
buhtz
quelle
0

Nachdem ich einige Zeit damit zu kämpfen hatte, kam ich schließlich auf Folgendes, so dass das Konfigurationsobjekt nur einmal geladen wurde, wenn es von separaten Modulen aufgerufen wurde. Mit der Metaklasse kann eine globale Klasseninstanz im eingebauten Diktat gespeichert werden. Dies scheint derzeit die beste Möglichkeit zu sein, ein ordnungsgemäßes globales Programm zu speichern.

import builtins

# -----------------------------------------------------------------------------
# So..... you would expect that a class would be "global" in scope, however
#   when different modules use this,
#   EACH ONE effectively has its own class namespace.  
#   In order to get around this, we use a metaclass to intercept
#   "new" and provide the "truly global metaclass instance" if it already exists

class MetaConfig(type):
    def __new__(cls, name, bases, dct):
        try:
            class_inst = builtins.CONFIG_singleton

        except AttributeError:
            class_inst = super().__new__(cls, name, bases, dct)
            builtins.CONFIG_singleton = class_inst
            class_inst.do_load()

        return class_inst

# -----------------------------------------------------------------------------

class Config(metaclass=MetaConfig):

    config_attr = None

    @classmethod
    def do_load(cls):
        ...<load-cfg-from-file>...
Den-Jason
quelle
-1

Ich kann mich nicht erinnern, wo ich diese Lösung gefunden habe, aber ich finde sie aus Sicht von Nicht-Python-Experten am elegantesten:

class SomeSingleton(dict):
    __instance__ = None
    def __new__(cls, *args,**kwargs):
        if SomeSingleton.__instance__ is None:
            SomeSingleton.__instance__ = dict.__new__(cls)
        return SomeSingleton.__instance__

    def __init__(self):
        pass

    def some_func(self,arg):
        pass

Warum mag ich das? Keine Dekoratoren, keine Metaklassen, keine Mehrfachvererbung ... und wenn Sie entscheiden, dass es kein Singleton mehr sein soll, löschen Sie einfach die __new__Methode. Da ich neu in Python bin (und OOP im Allgemeinen), erwarte ich, dass mich jemand klar macht, warum dies ein schrecklicher Ansatz ist.

2cynykyl
quelle
2
Warum ist das ein schrecklicher Ansatz? Wenn Sie eine weitere Singleton-Klasse erstellen möchten, müssen Sie die kopieren und einfügen __new__. Wiederhole dich nicht .
GingerPlusPlus
Auch warum nimmt dein neues *argsund **kwargsund macht dann nichts mit ihnen? Geben Sie sie auf dict.__new__diese Weise weiter : dict.__new__(cls, *args, **kwargs).
GingerPlusPlus
Dies ruft die __init__Methode jedes Mal auf, wenn die Klasse aufgerufen wird. Wenn Ihre __init__Methode tatsächlich etwas getan hat, würden Sie das Problem bemerken. Wann immer Sie dies tun SomeSingleton(), wird der Status Ihres Singletons durch die __init__Methode zurückgesetzt.
Aran-Fey
-2

Dies ist meine bevorzugte Methode zur Implementierung von Singletons:

class Test(object):
    obj = None

    def __init__(self):
        if Test.obj is not None:
            raise Exception('A Test Singleton instance already exists')
        # Initialization code here

    @classmethod
    def get_instance(cls):
        if cls.obj is None:
            cls.obj = Test()
        return cls.obj

    @classmethod
    def custom_method(cls):
        obj = cls.get_instance()
        # Custom Code here
fabelhaft
quelle
1
Dies ist nicht unbedingt ein Singleton, da mehr als eine Instanz der Klasse vorhanden sein kann. Eine Verbesserung wäre, die Klasse nicht initialisierbar zu machen und alle Klassenmethoden auf Klassenattribute einwirken zu lassen
theheadofabroom
-2

Diese Antwort ist wahrscheinlich nicht das, wonach Sie suchen. Ich wollte einen Singleton in dem Sinne, dass nur dieses Objekt zum Vergleich seine Identität hatte. In meinem Fall wurde es als Sentinel-Wert verwendet . Auf die die Antwort sehr einfach ist, machen Sie ein beliebiges Objekt mything = object()und nach Pythons Natur wird nur dieses Ding seine Identität haben.

#!python
MyNone = object()  # The singleton

for item in my_list:
    if item is MyNone:  # An Example identity comparison
        raise StopIteration
ThorSummoner
quelle
Ich habe gelernt, dass Module tatsächlich mehrmals importiert werden können. In diesem Fall ist dies nur ein lokaler Singleton, der in keiner Weise wirklich ein Singleton ist.
ThorSummoner
Können Sie erläutern, wie ein Modul mehrmals importiert werden kann? Das einzige Mal, dass ich gesehen habe, dass eine Ausnahme auftritt, während das Modul geladen wird, kann der Benutzer das Modul später noch laden, die Nebenwirkungen sind jedoch bereits aufgetreten, sodass einige Aktionen möglicherweise ein zweites Mal ausgeführt werden.
Sleblanc
Sobald ein Modul vollständig geladen wurde, sehe ich keine Möglichkeit, dieses Modul erneut auszuführen, außer indem der Interpreter dazu gezwungen wird, dies mit evaloder zu tun importlib.reload.
Sleblanc
-3

Diese Lösung verursacht eine gewisse Verschmutzung des Namespaces auf Modulebene (drei Definitionen statt nur einer), aber ich finde es einfach zu folgen.

Ich würde gerne so etwas schreiben können (verzögerte Initialisierung), aber leider sind Klassen im Körper ihrer eigenen Definitionen nicht verfügbar.

# wouldn't it be nice if we could do this?
class Foo(object):
    instance = None

    def __new__(cls):
        if cls.instance is None:
            cls.instance = object()
            cls.instance.__class__ = Foo
        return cls.instance

Da dies nicht möglich ist, können wir die Initialisierung und die statische Instanz in aufteilen

Eifrige Initialisierung:

import random


class FooMaker(object):
    def __init__(self, *args):
        self._count = random.random()
        self._args = args


class Foo(object):
    def __new__(self):
        return foo_instance


foo_instance = FooMaker()
foo_instance.__class__ = Foo

Faule Initialisierung:

Eifrige Initialisierung:

import random


class FooMaker(object):
    def __init__(self, *args):
        self._count = random.random()
        self._args = args


class Foo(object):
    def __new__(self):
        global foo_instance
        if foo_instance is None:
            foo_instance = FooMaker()
        return foo_instance


foo_instance = None
Gregory Nisbet
quelle