Gibt es eine einfache und elegante Möglichkeit, Singletons zu definieren?[Duplikat]
-
09-06-2019 - |
Frage
Auf diese Frage gibt es hier bereits eine Antwort:
- Erstellen eines Singletons in Python 20 Antworten
Es scheint viele Möglichkeiten zu geben, es zu definieren Singletons in Python.Gibt es eine einheitliche Meinung zu Stack Overflow?
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:
Sie müssen Klassen neuen Stils verwenden (abgeleitet von
object
) dafür.Der Singleton wird bei seiner Definition initialisiert und nicht bei der ersten Verwendung.
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)