L'utilisation d'une Métaclasse
Je recommande la Méthode #2, mais il est préférable d'utiliser une métaclasse qu'une classe de base. Voici un exemple de mise en œuvre:
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
Ou en Python3
class Logger(metaclass=Singleton):
pass
Si vous souhaitez exécuter l' __init__
chaque fois que la classe est appelée, ajouter
else:
cls._instances[cls].__init__(*args, **kwargs)
à l' if
déclaration en Singleton.__call__
.
Quelques mots sur les metaclasses. Une métaclasse est la classe d'une classe; c'est-à-dire, une classe est une instance de sa métaclasse. Vous trouverez la métaclasse d'un objet en Python, avec type(obj)
. Normale de nouvelle-classes de style sont de type type
. Logger
dans le code ci-dessus sera de type class 'your_module.Singleton'
, tout comme le (seul) exemple d' Logger
sera de type class 'your_module.Logger'
. Lorsque vous appelez enregistreur, Logger()
, Python première demande à la métaclasse d' Logger
, Singleton
,, que faire, permettant la création de l'instance pour être désamorcée. Ce processus est le même que Python demandant une classe quoi faire en appelant __getattr__
lorsque vous faites référence à l'un de ses attributs en effectuant myclass.attribute
.
Une métaclasse essentiellement décide que la définition d'une classe et comment mettre en œuvre cette définition. Voir, par exemple, http://code.activestate.com/recipes/498149/, qui, essentiellement, recrée de style C struct
s en Python à l'aide de metaclasses. Le thread Quels sont vos (béton) des cas d'utilisation pour les metaclasses en Python? fournit également quelques exemples, ils semblent généralement être liées à la programmation déclarative, en particulier dans les Formulaires.
Dans cette situation, si vous utilisez votre Méthode #2, et une sous-classe définit un __new__
méthode, il sera exécuté à chaque fois que vous appelez SubClassOfSingleton()
-- parce qu'il est responsable de l'appel de la méthode qui retourne la stockées instance. Avec une métaclasse, il ne sera appelée qu'une fois, lors de la seule instance est créée. Vous souhaitez personnaliser ce que cela signifie d'appel de la classe, qui est décidé par son type.
En général, il fait sens pour utiliser une métaclasse pour implémenter un singleton. Un singleton est spécial, car il est créé qu'une seule fois, et une métaclasse est la façon dont vous pouvez personnaliser la création d'une classe. À l'aide d'une métaclasse vous donne plus de contrôle dans le cas où vous avez besoin de personnaliser le singleton définitions de classe dans d'autres façons.
Votre singletons n'aurez pas besoin de l'héritage multiple (parce que la métaclasse est pas une classe de base), mais pour les sous-classes de la classe créée que l'utilisation de l'héritage multiple, vous devez vous assurer que la classe singleton est le premier / la plus à gauche un avec une métaclasse qui redéfinit __call__
C'est très peu probable d'être un problème. L'instance dict est pas dans l'instance de l'espace de noms afin de ne pas écraser accidentellement.
Vous entendrez aussi que le pattern singleton viole le "Principe de Responsabilité Unique", chaque classe devrait faire qu'une seule chose. De cette façon, vous n'avez pas à vous soucier de gâcher une chose que le code ne si vous avez besoin de changer l'autre, parce qu'ils sont séparés et encapsulé. La métaclasse de mise en œuvre de passe ce test. La métaclasse est responsable de l'application de la répétition et à la création de la classe et sous-classes n'ont pas besoin d'être conscients qu'ils sont des singletons. Méthode #1 ne réussit pas ce test, comme vous l'avez remarqué avec "MyClass lui-même est une fonction, pas une classe, de sorte que vous ne pouvez pas appeler les méthodes de la classe."
Python 2 Et 3 Version Compatible
écrire du code qui fonctionne dans les deux Python2 et 3 à l'aide d'un peu plus compliqué régime. Depuis metaclasses sont généralement des sous-classes de type type
, il est possible d'en utiliser un pour créer dynamiquement un intermédiaire de la classe de base au moment de la compilation avec elle comme sa métaclasse et ensuite l'utiliser comme un baseclass du public singleton de la classe de base. C'est plus difficile à expliquer qu'à faire, comme l'illustre la suivante:
# works in Python 2 & 3
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 Singleton(_Singleton('SingletonMeta', (object,), {})): pass
class Logger(Singleton):
pass
Un aspect ironique de cette approche est que c'est à l'aide de sous-classement pour mettre en œuvre une métaclasse. Un avantage possible est que, contrairement à une pure métaclasse, isinstance(inst, Singleton)
sera de retour True
.
Corrections
Sur un autre sujet, vous avez probablement déjà remarqué, mais la classe de base de la mise en œuvre dans votre post original est faux. _instances
doit être référencé sur la classe, vous devez utiliser super()
ou vous êtes recursing, et __new__
est en fait une méthode statique que vous avez à passer à la classe, pas une méthode de classe, comme la classe réelle n'a pas été créé mais, lorsqu'il est appelé. Toutes ces choses vont être remplies pour qu'une métaclasse mise en œuvre.
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()
Décorateur De Retour D'Une Classe
J'ai d'abord été la rédaction d'un commentaire, mais il était trop long, donc je vais ajouter ici. Méthode n ° 4 est mieux que l'autre décorateur version, mais c'est que le code nécessaire pour un singleton, et il n'est pas clair ce qu'il fait.
Les principaux problèmes de la tige à partir de la classe c'est d'être propre de la classe de base. Tout d'abord, n'est-il pas bizarre d'avoir une classe d'être une sous-classe de presque identiques de la classe du même nom, qui n'existe que dans sa __class__
d'attribut? Cela signifie également que vous ne pouvez pas définir toutes les méthodes que l'appel à la méthode de même nom dans leur classe de base avec super()
parce qu'ils répète. Cela signifie que votre classe ne peut pas personnaliser __new__
, et ne peut pas tirer de toutes les classes qui ont besoin d' __init__
appelé sur eux.
Quand utiliser le pattern singleton
Votre cas d'utilisation est l'un des meilleurs exemples de vouloir utiliser un singleton. Vous dites dans l'un des commentaires "Pour me connecter a toujours semblé un candidat naturel pour les Singletons." Vous avez tout à fait raison.
Quand les gens disent que les singletons sont mauvais, la raison la plus commune est qu'ils sont implicites de l'état partagé. Alors qu'avec des variables globales et de haut niveau module importations sont explicites partagée de l'état, d'autres objets qui sont passés autour sont généralement instancié. C'est un bon point, à deux exceptions près.
La première, et celle qui est mentionné dans divers endroits, c'est lorsque les singletons sont des constantes. Utilisation de constantes globales, en particulier les énumérations, est largement accepté et considéré comme sain d'esprit, car n'importe quoi, aucun des utilisateurs peut mettre en place pour tous les autres utilisateurs. Ceci est également vrai pour une constante de singleton.
La seconde exception, qui mentionne moins, est à l'opposé -- lorsque le singleton est seulement une des données de l'évier, pas une source de données (directement ou indirectement). C'est pourquoi les enregistreurs de se sentir comme un "naturel" utiliser pour les singletons. Comme les différents utilisateurs sont de ne pas changer les bûcherons , par des moyens autres utilisateurs ne se soucient, il n'y a pas vraiment partagé de l'état. Cela supprime le principal argument contre le pattern singleton, et en fait un choix raisonnable en raison de leur facilité d'utilisation pour la tâche.
Voici une citation de http://googletesting.blogspot.com/2008/08/root-cause-of-singletons.html:
Maintenant, il y a une sorte de Singleton qui est OK. C'est un singleton où tous les accessible des objets immuables. Si tous les objets sont immuables que Singleton n'a pas d'état global, comme tout ce qui est constante. Mais il est si facile de tourner ce genre de singleton dans mutable, il est très pente glissante. Donc, je suis contre ces Singletons trop, non pas parce qu'ils sont mauvais, mais parce qu'il est très facile pour eux d'aller mal. (Comme une note de côté Java énumération sont juste ce genre de singletons. Tant que vous ne mettez pas de l'état dans votre énumération, vous êtes OK, merci donc de ne pas.)
L'autre type de Singletons, qui sont semi-acceptables sont ceux qui ne le sont pas l'effet de l'exécution de votre code, Ils n'ont pas "d'effets secondaires". La journalisation est l'exemple parfait. Il est chargé avec des Singletons et de l'état global. Il est acceptable (comme il ne sera pas vous faire du mal) parce que l'application ne se comporte pas du tout différent si un enregistreur est activé. Le flux d'informations ici un sens: à Partir de votre application dans l'enregistreur. Même pensé que les bûcherons sont mondial de l'etat, depuis pas de flux d'informations à partir de bûcherons dans votre application, les bûcherons sont acceptables. Vous devriez toujours injecter votre enregistreur si vous voulez que votre test d'affirmer que quelque chose a été enregistré, mais en général, les Bûcherons ne sont pas dangereux en dépit d'être plein de l'etat.