Gibt es eine einfache, elegante Möglichkeit, Singletons zu definieren? [duplicate]

Lesezeit: 11 Minuten

Benutzer-Avatar
Jamie

Es scheint viele Definitionsmöglichkeiten zu geben Singles in Python. Gibt es eine übereinstimmende Meinung zu Stack Overflow?

  • Singletons sind pathologische Lügnernicht wahr?

    – Jonas Byström

    5. Juli 2012 um 15:01 Uhr

  • “Diese Frage passt nicht gut zu unserem Q & A-Format” – Ich denke, dies ist keine subjektive Frage. Gibt es eine Möglichkeit, solche Fragen so zu stellen, dass sie in das SO Q & A-Format passen?

    – binithb

    13. September 2014 um 22:13 Uhr


  • Ich stimme nicht zu, dass dies nicht konstruktiv ist. Könnte es wieder geöffnet werden, wenn es verschoben wird programers.stackexchange.com ?

    – langlauf.io

    6. August 2015 um 9:35 Uhr


  • @stackoverflowwww nein, weil es meinungsbasiert ist und progs.SE das nicht mag.

    – Ratschenfreak

    6. August 2015 um 9:44 Uhr

  • @ratchetfreak Was die Frage populär macht, ist, dass Leute wie ich nach verschiedenen Möglichkeiten suchen, Singletons in Python zu erstellen. Es gibt Alternativen mit Vor- und Nachteilen oder die nur in bestimmten Situationen geeignet sind. Die Frage könnte umformuliert werden a la “Welche verschiedenen Möglichkeiten gibt es in Python, um ein Singleton zu erstellen? Mich interessiert besonders der Unterschied zwischen Lösungen, die auf einer Klasse basieren, und solchen, die auf einer Klasseninstanz basieren.”

    – langlauf.io

    6. August 2015 um 9:49 Uhr


Benutzer-Avatar
Staale

Ich sehe nicht wirklich die Notwendigkeit, da ein Modul mit Funktionen (und keine 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, sodass Sie sich nicht vor mehreren Instanziierungen schützen können, außer nur durch Konventionen bei der Verwendung Ihrer API. Ich würde immer noch Methoden in ein Modul einfügen und das Modul als Singleton betrachten.

  • Könnte der Konstruktor nicht einfach prüfen, ob bereits eine Instanz erstellt wurde, und eine Ausnahme auslösen, wenn dies der Fall ist?

    – Casebash

    16. Mai 2010 um 7:56 Uhr

  • Dies ist in Ordnung, solange Sie die Vererbung nicht als Teil Ihres Entwurfs verwenden müssen. In diesem Fall sind die meisten der folgenden Antworten besser geeignet

    – Jim Jeffries

    6. Juni 2011 um 8:23 Uhr

  • Es ist kaputt, wenn Sie einen zyklischen Import haben

    – sterbend

    25. September 2012 um 10:49 Uhr

  • Was werde ich tun, wenn ich möchte, dass dieses Modul vererbbar ist?

    – Yossi

    26. November 2012 um 9:09 Uhr

  • Das ist meiner Meinung nach falsch. Ein Ärgernis bei Schnittstellen auf Modulebene ist die Verwaltung der Importe. Zum Beispiel Python logging ist eine Schnittstelle auf Modulebene. Um sicherzustellen, dass Sie danach vollständig aufräumen logging du musst anrufen logging.shutdown(). Dies bedeutet, dass Sie importieren müssen logging in das aufrufende Modul shutdown. Wenn es sich um ein Singleton-Muster handelt, ist es möglich, shutdown für die Instanz in jedem Modul aufzurufen, an das es übergeben wird.

    – Matt

    25. Februar 2016 um 6:49 Uhr

Benutzer-Avatar
Paul Manta

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

  • Da Python im Lieferumfang enthalten ist, sollte dies Teil von a sein desing_pattern Standardbibliothek, danke

    – schneidig

    31. August 2014 um 0:23 Uhr

  • @akhan Ich habe mich entschieden, Konstruktoren absichtlich nicht mit Argumenten zu unterstützen, da die Argumente nur beim ersten Mal verwendet und alle anderen Male ignoriert werden. Dadurch kann es sehr schwierig werden, Ihrem Code zu folgen, da Sie möglicherweise an verschiedenen Stellen unterschiedliche Argumente verwenden, aber möglicherweise nicht wissen, welcher dieser Aufrufe derjenige ist, der das Singleton tatsächlich initialisiert.

    – Paul Manta

    28. April 2017 um 7:53 Uhr

  • @akhan Wenn Sie Ihren Singleton wirklich mit Argumenten initialisieren möchten, sollten Sie einen separaten haben initialize() Methode, die beliebige Argumente und Würfe annehmen kann, wenn sie mehr als einmal aufgerufen wird.

    – Paul Manta

    28. April 2017 um 7:54 Uhr

  • Das ist ein wirklich schlecht Singleton-Implementierung. Erstens ist es kein richtiger Dekorateur, weil es nicht verwendet wird functools.wraps oder functools.update_wrapper. Zweitens muss die Instanz per Anruf abgerufen werden Foo.Instance() ist schrecklich unpythonisch und es gibt genau 0 Gründe, warum es nicht so implementiert werden konnte Foo() stattdessen. Drittens führt das Ersetzen der Klasse auf diese Weise zu unerwarteten Ergebnissen wie type(Foo.instance()) is Foo -> False

    – Aran-Fey

    28. Mai 2018 um 12:59 Uhr

  • @Aran-Fey scheint, als würde diese Lösung Ihre Blase wirklich zum Platzen bringen, lol. Ich glaube nicht, dass Paul Manta jemals gesagt hat, dass dies die beste Lösung weltweit ist. Er versuchte nur, die Frage des ursprünglichen Autors zu beantworten. Ich denke, es ist eine großartige Lösung für ein “Mangel davon” in Python.

    – JokerMartini

    5. Juli 2019 um 15:17 Uhr

Benutzer-Avatar
jojo

Sie können die überschreiben __new__ Methode so:

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"

  • WARNUNG: Wenn __new__() eine Instanz von cls zurückgibt, wird die Methode __init__() der neuen Instanz wie __init__(self[, …]), wobei self die neue Instanz ist und die verbleibenden Argumente die gleichen sind, die an __new__() übergeben wurden. Wenn eine Unterklasse von Singleton __init__() implementiert, wird sie mehrmals mit demselben Selbst aufgerufen. Ich landete stattdessen mit einer Fabrik.

    – alsuren

    8. September 2011 um 12:48 Uhr


  • Dies wäre besser, wenn Sie hier eine Metaklasse als Antwort verwenden: stackoverflow.com/a/33201/804147

    – unterlaufen

    23. Januar 2012 um 15:24 Uhr

  • Dies gibt die folgende Warnung aus – singleton.py:9: DeprecationWarning: object.__new__() takes no parameters cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)

    – Siddhant

    2. Januar 2013 um 16:17 Uhr


  • @Siddhant: Schlimmer noch, in Python 3 wird diese Warnung zu einem Fehler. Sehen bugs.python.org/issue1683368 und blog.jaraco.com/2014/05/… für mehr Details.

    – Jason R. Coombs

    14. Juni 2014 um 16:36 Uhr

  • Ein weiteres Problem besteht darin, diese Implementierung in Unterklassen umzuwandeln. Unterklassen enthalten einen Verweis auf die Oberklasse in _instance, wenn es zuvor instanziiert wurde (aber nicht umgekehrt). Um das zu beheben, können Sie ersetzen if not cls._instance mit if type(cls._instance) != cls (was funktioniert, weil None ist nicht Singleton und Singleton ist nicht SubSingleton(Singleton)).

    – rel

    15. Juni um 10:39 Uhr


Benutzer-Avatar
Peter Hofmann

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, dieselbe Identität zu haben, teilen sie sich den Status.

Benutzer-Avatar
Zugespitzt

Der Modulansatz funktioniert gut. Wenn ich unbedingt ein 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

  • Dieses Muster verstößt gegen das „Single Responsibility Principle“ (c2.com/cgi/wiki?SingleResponsibilityPrinciple). Siehe Punkt (2) in blogs.msdn.com/scottdensmore/archive/2004/05/25/140827.aspx.

    – Haridsv

    20. Mai 2010 um 19:21 Uhr

  • @haridsv Ich stimme nicht zu. Die Tatsache, dass die Klasse ein Singleton ist ist in der Metaklassenimplementierung abstrahiert — die Klasse selbst weiß oder kümmert sich nicht darum, dass sie ein Singleton ist, da sie nicht für die Durchsetzung dieser Anforderung zuständig ist, die Metaklasse ist es. Die folgende Methode ist jedoch eindeutig ein Verstoß, wie Sie bemerken. Die Methode der Basisklasse liegt irgendwo dazwischen.

    – agf

    23. Juli 2011 um 4:38 Uhr

  • @dare2be: Könnte das von Ihnen erwähnte Kopierproblem nicht einfach dadurch behoben werden, dass die Metaklasse auch a hinzufügt __deepcopy__() Methode zur erstellten Klasse?

    – Martinau

    3. Dezember 2012 um 18:18 Uhr

  • @martineau: Das ist type.__init__ es überschreibt, nicht MyClass.__init__

    – Erich

    3. Februar 2014 um 0:00 Uhr

  • Ein anderer Stackoverflow-Kommentar erwähnte, dass Sie diesen Fehler durch Überschreiben beheben können new__() “` Klasse SingletonMeta(Typ): def _new__(cls, name, bases, dict): dict[‘_deepcopy‘] = Diktat[‘copy‘] = lambda self, *args: self return super(SingletonMeta, cls).__new__(cls, name, bases, dict) “` – stackoverflow.com/a/9887928/748503

    – James McGuigan

    24. November 2017 um 18:38 Uhr

Benutzer-Avatar
Peter Mortensen

Sehen Sie sich diese Implementierung an PEP318Implementierung des Singleton-Musters mit einem Dekorateur:

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

@singleton
class MyClass:
    ...

  • Dieses Muster verstößt gegen das „Single Responsibility Principle“ (c2.com/cgi/wiki?SingleResponsibilityPrinciple). Siehe Punkt (2) in blogs.msdn.com/scottdensmore/archive/2004/05/25/140827.aspx.

    – Haridsv

    20. Mai 2010 um 19:21 Uhr

  • @haridsv Ich stimme nicht zu. Die Tatsache, dass die Klasse ein Singleton ist ist in der Metaklassenimplementierung abstrahiert — die Klasse selbst weiß oder kümmert sich nicht darum, dass sie ein Singleton ist, da sie nicht für die Durchsetzung dieser Anforderung zuständig ist, die Metaklasse ist es. Die folgende Methode ist jedoch eindeutig ein Verstoß, wie Sie bemerken. Die Methode der Basisklasse liegt irgendwo dazwischen.

    – agf

    23. Juli 2011 um 4:38 Uhr

  • @dare2be: Könnte das von Ihnen erwähnte Kopierproblem nicht einfach dadurch behoben werden, dass die Metaklasse auch a hinzufügt __deepcopy__() Methode zur erstellten Klasse?

    – Martinau

    3. Dezember 2012 um 18:18 Uhr

  • @martineau: Das ist type.__init__ es überschreibt, nicht MyClass.__init__

    – Erich

    3. Februar 2014 um 0:00 Uhr

  • Ein anderer Stackoverflow-Kommentar erwähnte, dass Sie diesen Fehler durch Überschreiben beheben können new__() “` Klasse SingletonMeta(Typ): def _new__(cls, name, bases, dict): dict[‘_deepcopy‘] = Diktat[‘copy‘] = lambda self, *args: self return super(SingletonMeta, cls).__new__(cls, name, bases, dict) “` – stackoverflow.com/a/9887928/748503

    – James McGuigan

    24. November 2017 um 18:38 Uhr

Das Python-Dokumentation deckt das 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
        """
        it_id = "__it__"
        # getattr will dip into base classes, so __dict__ must be used
        it = cls.__dict__.get(it_id, None)
        if it is not None:
            return it
        it = object.__new__(cls)
        setattr(cls, it_id, it)
        it.init(*args, **kwds)
        return it

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


class A(Singleton):
    pass


class B(Singleton):
    pass


class C(A):
    pass


assert A() is A()
assert B() is B()
assert C() is C()
assert A() is not B()
assert C() is not B()
assert C() is not A()

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")]
        ...

  • +1, weil er der einzige ist, der die Implementierung von Guido van Rossum erwähnt. Ihre eigene Version ist jedoch falsch: Sie sollten nicht verwenden hasattr und getattr Innerhalb __new__ wie sie beide rufen object.__getattribute__ die wiederum Ihre nachschlägt "__self__" -Attribut durch die gesamte Klassenhierarchie statt nur durch die aktuelle Klasse. Wenn Guido verwendet __dict__ für den Attributzugriff, der einen Grund hat. Versuchen: class A(GuidoSingleton): pass, class B(A): pass, class C(YourSingleton): pass, class D(C): pass, print(A(), B(), C(), D()). Alle Unterklassen verweisen auf dieselbe Instanz mit YourSingleton!

    – Maggyero

    15. Dezember 2017 um 16:56 Uhr

  • +1, um uns daran zu erinnern, dass die Python-Dokumentation immer der beste Ort ist, um mit der Suche nach Singleton- und anderen Entwurfsmustern zu beginnen.

    – Benutzer-Sternchen

    13. November 2018 um 0:58 Uhr


1116720cookie-checkGibt es eine einfache, elegante Möglichkeit, Singletons zu definieren? [duplicate]

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy