type - singleton example python3




Créer un singleton en Python (13)

Utilisez une métaclasse

Je recommanderais la méthode n ° 2 , mais il vaut mieux utiliser une métaclasse plutôt 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 voulez exécuter __init__ chaque fois que la classe est appelée, ajoutez

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

à l'instruction if dans Singleton.__call__ .

Quelques mots sur les métaclasses. Une métaclasse est la classe d'une classe ; c'est-à-dire qu'une classe est une instance de sa métaclasse . Vous trouvez la métaclasse d'un objet en Python avec le type(obj) . Les classes normales de nouveau style sont de type type . Logger dans le code ci-dessus sera de type class 'your_module.Singleton' , tout comme l'instance (seulement) de Logger sera de type class 'your_module.Logger' . Lorsque vous appelez logger avec Logger() , Python demande d'abord à la métaclasse de Logger , Singleton , quoi faire, permettant de préempter la création d'instance. Ce processus est le même que Python demandant à une classe quoi faire en appelant __getattr__ quand vous référencez l'un de ses attributs en faisant myclass.attribute .

Une métaclasse décide essentiellement ce que signifie la définition d'une classe et comment implémenter cette définition. Voir par exemple http://code.activestate.com/recipes/498149/ , qui recrée essentiellement des struct style C dans Python en utilisant des métaclasses. Le fil Quels sont vos cas d'utilisation (concrets) pour les métaclasses en Python? fournit également quelques exemples, ils semblent généralement être liés à la programmation déclarative, en particulier dans les ORM.

Dans cette situation, si vous utilisez votre méthode n ° 2 et qu'une sous-classe définit une méthode __new__ , elle sera exécutée chaque fois que vous appelez SubClassOfSingleton() , car il est responsable de l'appel de la méthode qui renvoie l'instance stockée. Avec une métaclasse, elle ne sera appelée qu'une seule fois , lorsque la seule instance est créée. Vous voulez personnaliser ce que cela signifie d'appeler la classe , ce qui est décidé par son type.

En général, il est logique d'utiliser une métaclasse pour implémenter un singleton. Un singleton est spécial car créé une seule fois , et une métaclasse est la façon dont vous personnalisez la création d'une classe . L'utilisation d'une métaclasse vous donne plus de contrôle au cas où vous auriez besoin de personnaliser les définitions de classe singleton d'une autre manière.

Vos singletons n'auront pas besoin d'héritages multiples (car la métaclasse n'est pas une classe de base), mais pour les sous-classes de la classe créée qui utilisent l'héritage multiple, vous devez vous assurer que la classe singleton est la première / la plus à gauche avec une métaclasse __call__ Ceci est très peu susceptible d'être un problème. L'instance dict n'est pas dans l'espace de nom de l'instance, donc elle ne l'écrasera pas accidentellement.

Vous entendrez également que le modèle singleton viole le «principe de responsabilité unique» - chaque classe ne devrait faire qu'une seule chose . De cette façon, vous n'avez pas à vous soucier de casser une chose que le code fait si vous avez besoin de changer un autre, parce qu'ils sont séparés et encapsulés. L'implémentation de métaclasse réussit ce test . La métaclasse est responsable de l' application du modèle et la classe créée et les sous-classes n'ont pas besoin d'être conscientes qu'il s'agit de singletons . La méthode n ° 1 échoue à ce test, comme vous l'avez noté avec "MyClass lui-même est une fonction, pas une classe, donc vous ne pouvez pas appeler les méthodes de classe à partir de cela."

Version compatible Python 2 et 3

Ecrire quelque chose qui fonctionne à la fois dans Python2 et 3 nécessite d'utiliser un schéma un peu plus compliqué. Puisque les métaclasses sont généralement des sous-classes de type type , il est possible d'en utiliser une pour créer dynamiquement une classe de base intermédiaire à l'exécution avec sa métaclasse, puis l'utiliser comme baseclass de la classe de base publique Singleton . Il est plus difficile à expliquer qu'à faire, comme illustré ci-après:

# 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

Un aspect ironique de cette approche est qu'elle utilise la sous-classe pour implémenter une métaclasse. Un avantage possible est que, contrairement à une métaclasse pure, isinstance(inst, Singleton) retournera True .

Corrections

Sur un autre sujet, vous l'avez probablement déjà remarqué, mais l'implémentation de la classe de base dans votre message d'origine est erronée. _instances doit être référencé sur la classe , vous devez utiliser super() ou vous êtes récursif , et __new__ est en fait une méthode statique à laquelle vous devez passer la classe , pas une méthode de classe, car la classe réelle n'a pas été créé quand il est appelé. Toutes ces choses seront aussi vraies pour une implémentation de métaclasse.

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 retournant une classe

Au début, j'écrivais un commentaire mais c'était trop long, donc je vais ajouter ceci ici. La méthode 4 est meilleure que l'autre version du décorateur, mais c'est plus de code que nécessaire pour un singleton, et ce n'est pas aussi clair.

Les principaux problèmes proviennent de la classe étant sa propre classe de base. Premièrement, n'est-ce pas bizarre d'avoir une classe comme une sous-classe d'une classe presque identique avec le même nom qui existe seulement dans son attribut __class__ ? Cela signifie également que vous ne pouvez pas définir de méthodes qui appellent la méthode du même nom sur leur classe de base avec super() car elles se recurciront. Cela signifie que votre classe ne peut pas personnaliser __new__ , et ne peut pas dériver des classes qui ont besoin de __init__ .

Quand utiliser le motif singleton

Votre cas d'utilisation est l' un des meilleurs exemples de vouloir utiliser un singleton. Vous dites dans l'un des commentaires "Pour moi, l'enregistrement a toujours semblé un candidat naturel pour Singletons." Tu as absolument raison .

Quand les gens disent que les singletons sont mauvais, la raison la plus courante est qu'ils sont des états partagés implicites . Alors que les variables globales et les importations de modules de niveau supérieur sont des états partagés explicites , les autres objets transmis sont généralement instanciés. C'est un bon point, à deux exceptions près .

Le premier, et celui qui est mentionné dans divers endroits, est quand les singletons sont constants . L'utilisation de constantes globales, en particulier d'énumérations, est largement acceptée et considérée comme raisonnable car, quoi qu'il arrive , aucun des utilisateurs ne peut les confondre avec un autre utilisateur . Ceci est également vrai pour un singleton constant.

La deuxième exception, qui est mentionnée moins, est le contraire - quand le singleton est seulement un puits de données , pas une source de données (directement ou indirectement). C'est pourquoi les bûcherons se sentent comme un usage «naturel» pour les singletons. Comme les différents utilisateurs ne modifient pas les enregistreurs de la même manière que les autres utilisateurs, il n'y a pas vraiment d'état partagé . Cela annule l'argument principal contre le modèle 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 un type de Singleton qui est OK. C'est un singleton où tous les objets atteignables sont immuables. Si tous les objets sont immuables, Singleton n'a pas d'état global, car tout est constant. Mais il est si facile de transformer ce genre de singleton en mutable, c'est une pente très glissante. Par conséquent, je suis contre ces Singletons aussi, pas parce qu'ils sont mauvais, mais parce qu'il est très facile pour eux d'aller mal. (En guise de remarque, l'énumération Java est juste ce genre de singletons: tant que vous ne mettez pas l'état dans votre énumération, vous êtes OK, alors s'il vous plaît ne le faites pas.)

Les autres types de singletons, qui sont semi-acceptables, sont ceux qui n'influent pas sur l'exécution de votre code. Ils n'ont pas d'effets secondaires. La journalisation est un exemple parfait. Il est chargé de singletons et d'état global. C'est acceptable (comme cela ne vous nuira pas) parce que votre application ne se comporte pas différemment si un enregistreur donné est activé ou non. L'information ici coule d'une façon: De votre application dans l'enregistreur. Même les enregistreurs de pensée sont des états globaux car aucune information ne circule des enregistreurs dans votre application, les enregistreurs sont acceptables. Vous devriez toujours injecter votre enregistreur si vous voulez que votre test affirme que quelque chose est enregistré, mais en général les enregistreurs ne sont pas nuisibles bien qu'ils soient pleins d'état.

Cette question n'est pas pour la discussion de savoir si le modèle de conception singleton est souhaitable, est un anti-modèle, ou pour toute guerre religieuse, mais pour discuter de la façon dont ce modèle est mieux mis en œuvre en Python de la façon la plus pythonique. Dans ce cas, je définis «le plus pythonique» pour signifier qu'il suit le «principe du moindre étonnement» .

J'ai plusieurs classes qui deviendraient singletons (mon cas d'utilisation est pour un enregistreur, mais ce n'est pas important). Je ne veux pas encombrer plusieurs classes avec gogo supplémentaire 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

Avantages

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

Les inconvénients

  • Alors que les objets créés en utilisant MyClass () seraient de vrais objets singleton, MyClass elle-même est une fonction, pas une classe, donc vous ne pouvez pas appeler les méthodes de classe à partir de celle-ci. Aussi pour m = MyClass(); n = MyClass(); o = type(n)(); 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

Avantages

  • C'est un vrai cours

Les inconvénients

  • Héritage multiple - eugh! __new__ pourrait être remplacé pendant l'héritage d'une deuxième classe de base? Il faut penser plus que nécessaire.

Méthode 3: Une metaclass

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

Avantages

  • C'est un vrai cours
  • Auto-magiquement couvre l'héritage
  • Utilise __metaclass__ pour son bon usage (et m'en a fait prendre conscience)

Les inconvénients

  • Y a-t-il?

Méthode 4: décorateur retournant une classe avec le 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

Avantages

  • C'est un vrai cours
  • Auto-magiquement couvre l'héritage

Les inconvénients

  • N'y a-t-il pas un surcoût pour créer chaque nouvelle classe? Ici, nous créons deux classes pour chaque classe que nous voulons faire un singleton. Bien que ce soit bien dans mon cas, je crains que cela ne puisse pas évoluer. Bien sûr, il y a un débat sur la question de savoir s'il est trop facile d'adapter ce modèle ...
  • Quel est le but de l'attribut _sealed
  • Impossible d'appeler des méthodes du même nom sur les classes de base en utilisant super() car elles se recurciront. Cela signifie que vous ne pouvez pas personnaliser __new__ et que vous ne pouvez pas sous-classer une classe dont vous avez besoin pour appeler __init__ .

Code basé sur la réponse de Tolli .

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

Explication:

  1. Créer une nouvelle classe, héritant de cls donnés
    (il ne modifie pas les cls au cas où quelqu'un voudrait par exemple singleton(list) )

  2. Créer une instance Avant de surcharger __new__ c'est si facile.

  3. Now, when we have easily created instance, overrides __new__ using method defined moment ago.
  4. The function returns instance only when it's what the caller expects, otherwise raises TypeError .
    The condition is not met when someone attempts to inherit from decorated class.

  5. If __new__() returns an instance of cls , then the new instance's __init__() method will be invoked like __init__(self[, ...]) , where self is the new instance and the remaining arguments are the same as were passed to __new__() .

    instance is already initialized, so function replaces __init__ with function doing nothing.

See it working online


Eh bien, à part d'être d'accord avec la suggestion générale de Pythonic sur le fait d'avoir un niveau global au niveau du module, qu'en est-il de ceci:

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)

La sortie est:

111     # the __init__ is called only on the 1st time
MyClass # the __name__ is preserved
True    # this is actually the same instance

Je ne me souviens pas où j'ai trouvé cette solution, mais je trouve que c'est la plus «élégante» de mon point de vue non-expert en Python:

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

Pourquoi est-ce que j'aime ça? Pas de décorateurs, pas de méta-classes, pas d'héritage multiple ... et si vous décidez que vous ne voulez plus que ce soit un Singleton, supprimez simplement la méthode __new__ . Comme je suis nouveau à Python (et OOP en général), je m'attends à ce que quelqu'un me pose la question de savoir pourquoi c'est une approche terrible?


Que dis-tu de ça:

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

Utilisez-le comme un décorateur sur une classe qui devrait être un singleton. Comme ça:

@singleton
class MySingleton:
    #....

Ceci est similaire au décorateur singleton = lambda c: c() dans une autre réponse. Comme l'autre solution, la seule instance a le nom de la classe ( MySingleton ). Cependant, avec cette solution, vous pouvez toujours "créer" des instances (en réalité obtenir la seule instance) de la classe, en faisant MySingleton() . Cela vous empêche également de créer des instances supplémentaires en faisant type(MySingleton)() (qui renvoie également la même instance).


Un paquebot (je ne suis pas fier, mais ça fait l'affaire):

class Myclass:
  def __init__(self):
      # do your stuff
      globals()[type(self).__name__] = lambda: self # singletonify

Voici ma propre implémentation de singletons. Tout ce que vous avez à faire est de décorer la classe; pour obtenir le singleton, vous devez utiliser la méthode Instance . Voici un exemple:

   @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

Et voici le 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)

Voici un one-liner pour vous:

singleton = lambda c: c()

Voici comment vous l'utilisez:

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

assert wat.get_x() == 1

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


Vous n'avez probablement jamais besoin d'un singleton en Python. Définissez simplement toutes vos données et fonctions dans un module et vous avez un singleton de facto.

Si vous avez absolument besoin d'un cours de singleton, j'irais avec:

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

my_singleton = My_Singleton()

Utiliser:

from mysingleton import my_singleton
my_singleton.foo()

où mysingleton.py est votre nom de fichier dans lequel My_Singleton est défini. Cela fonctionne car après la première importation d'un fichier, Python ne ré-exécute pas le code.


I'll toss mine into the ring. It's a simple decorator.

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.

Benefits I think it has over some of the other solutions:

  • It's clear and concise (to my eye ;D).
  • Its action is completely encapsulated. You don't need to change a single thing about the implementation of YourClass . This includes not needing to use a metaclass for your class (note that the metaclass above is on the factory, not the "real" class).
  • It doesn't rely on monkey-patching anything.
  • It's transparent to callers:
    • Callers still simply import YourClass , it looks like a class (because it is), and they use it normally. No need to adapt callers to a factory function.
    • What YourClass() instantiates is still a true instance of the YourClass you implemented, not a proxy of any kind, so no chance of side effects resulting from that.
    • isinstance(instance, YourClass) and similar operations still work as expected (though this bit does require abc so precludes Python <2.6).

One downside does occur to me: classmethods and staticmethods of the real class are not transparently callable via the factory class hiding it. I've used this rarely enough that I've never happen to run into that need, but it would be easily rectified by using a custom metaclass on the factory that implements __getattr__() to delegate all-ish attribute access to the real class.

A related pattern I've actually found more useful (not that I'm saying these kinds of things are required very often at all) is a "Unique" pattern where instantiating the class with the same arguments results in getting back the same instance. Ie a "singleton per arguments". The above adapts to this well and becomes even more concise:

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

All that said, I do agree with the general advice that if you think you need one of these things, you really should probably stop for a moment and ask yourself if you really do. 99% of the time, YAGNI.


This answer is likely not what you're looking for. I wanted a singleton in the sense that only that object had its identity, for comparison to. In my case it was being used as a Sentinel Value . To which the answer is very simple, make any object mything = object() and by python's nature, only that thing will have its identity.

#!python
MyNone = object()  # The singleton

for item in my_list:
    if item is MyNone:  # An Example identity comparison
        raise StopIteration

This is my preferred way of implementing 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

class Foo(object):
     pass

some_global_variable = Foo()

Les modules ne sont importés qu'une seule fois, tout le reste est surchargé. N'utilisez pas de singletons et essayez de ne pas utiliser de globals.





metaclass