1713 votes

Création d’un singleton en python

Cette question n'est pas pour le débat de savoir si ou non le modèle de conception Singleton est souhaitable, est un anti-modèle, ou pour toute les guerres de religion, mais de discuter de la façon dont ce modèle est mis en œuvre en python dans une façon qui est plus pythonic. Dans ce cas, j'définir "la plupart des pythonic' pour dire qu'il suit le "principe de moindre étonnement'

J'ai plusieurs classes qui allait devenir les singletons (mon cas d'utilisation est un enregistreur de frappe, mais ce n'est pas important). Je ne souhaite pas encombrer plusieurs classes avec ajout de gumph quand je peux simplement hériter ou décorer.

Meilleures méthodes:


Méthode 1: Un décorateur

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

Pros

  • Les décorateurs sont des additifs dans une manière qui est souvent plus intuitive que l'héritage multiple.

Cons

  • Tandis que les objets créés à l'aide de MyClass() serait vrai pour les objets singleton, MyClass lui-même est une fonction, pas une classe, de sorte que vous ne pouvez pas appeler les méthodes de la classe. Aussi pour m = MyClass(); n = MyClass(); o = type(n)(); alors m == n && m != o && n != o

Méthode 2: Une classe de base

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

Pros

  • C'est une véritable classe

Cons

  • L'héritage Multiple - eugh! __new__ peut être remplacée au cours de l'héritage à partir d'une deuxième classe de base? Penser plus que nécessaire.

Méthode 3: Une métaclasse

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

Pros

  • C'est une véritable classe
  • L'Auto-magiquement couvre l'héritage
  • Utilise __metaclass__ de son but (Et qui m'a fait)

Cons

  • Y at-il?

Méthode 4: décorateur retour d'une classe du même nom

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

Pros

  • C'est une véritable classe
  • L'Auto-magiquement couvre l'héritage

Cons

  • N'est-il pas une charge pour la création de chaque nouvelle classe? Ici, nous sommes la création de deux classes pour chaque classe, nous souhaitons faire un singleton. Tout c'est très bien, dans mon cas, j'ai peur que ce ne serait pas à l'échelle. Bien sûr, il est un sujet de débat quant à savoir si elle promet d'être trop facile à l'échelle de ce modèle...
  • Quel est le point de l' _sealed d'attribut
  • Ne peut pas appeler des méthodes du même nom sur les classes de base à l'aide de super() parce qu'ils répète. Cela signifie que vous ne pouvez pas personnaliser __new__ et ne peut pas sous-classe d'une classe qui a besoin de vous pour appeler à l' __init__.

1137voto

agf Points 45052

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 structs 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.

155voto

Cat Plus Plus Points 53385
<pre><code></code><p>Modules sont importés en une seule fois, tout le reste est overthinking. N’utilisez les singletons et essayez de ne pas utiliser globals.</p></pre>

96voto

warvariuc Points 11787

Utiliser un module. Elle est importée en une seule fois. Définir quelques variables globales en elle - ils seront « attributs » singleton. Ajouter certaines fonctions - « méthodes » de singleton.

18voto

Jonas Kölker Points 4520

Voici un bon mot pour vous :

Voici comment l’utiliser :

Votre objet est instancié avec impatience. Cela peut ou peut ne pas être ce que vous voulez.

13voto

Anton Points 1743

Découvrez ce fil avec plusieurs solutions.

Je vous recommande vivement de regarder les pourparlers de Alex Martelli sur les design patterns en python : partie 1 et partie 2. En particulier, dans la partie 1, il parle d’objets d’État singletons/partagé.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X