Question

Cette question a déjà une réponse ici :

Il semble y avoir de nombreuses façons de définir célibataires en Python.Existe-t-il un consensus sur Stack Overflow ?

Était-ce utile?

La solution

Je n'en vois pas vraiment la nécessité, car un module avec des fonctions (et non une classe) servirait bien de singleton.Toutes ses variables seraient liées au module, qui ne pourrait de toute façon pas être instancié à plusieurs reprises.

Si vous souhaitez utiliser une classe, il n'existe aucun moyen de créer des classes privées ou des constructeurs privés en Python, vous ne pouvez donc pas vous protéger contre plusieurs instanciations, autrement que via la simple convention d'utilisation de votre API.Je mettrais toujours simplement des méthodes dans un module et considérerais le module comme le singleton.

Autres conseils

Voici ma propre implémentation de singletons.Tout ce que vous avez à faire est de décorer la classe ;pour obtenir le singleton, il faut alors utiliser le Instance méthode.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. 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)

Vous pouvez remplacer le __new__ méthode comme celle-ci :

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"

Une approche légèrement différente pour implémenter le singleton en Python est la motif borgne par Alex Martelli (employé de Google et génie Python).

class Borg:
    __shared_state = {}
    def __init__(self):
        self.__dict__ = self.__shared_state

Ainsi, au lieu de forcer toutes les instances à avoir la même identité, elles partagent un état.

L’approche modulaire fonctionne bien.Si j'ai absolument besoin d'un singleton, je préfère l'approche Metaclass.

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

Voir cette implémentation de PEP318, implémentant le modèle singleton avec un décorateur :

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

@singleton
class MyClass:
    ...

Comme le réponse acceptée dit, la manière la plus idiomatique est simplement de utiliser un module.

Dans cet esprit, voici une preuve de 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

Voir le Modèle de données Python pour plus de détails sur __new__.

Exemple:

@singleton
class Duck(object):
    pass

if Duck() is Duck():
    print "It works!"
else:
    print "It doesn't work!"

Remarques:

  1. Vous devez utiliser des classes de nouveau style (dérivées de object) pour ça.

  2. Le singleton est initialisé lorsqu'il est défini, plutôt que lors de sa première utilisation.

  3. Ceci est juste un exemple de jouet.Je ne l'ai jamais utilisé dans le code de production et je n'ai pas l'intention de le faire.

Je n'en suis pas sûr, mais mon projet utilise des « singletons conventionnels » (et non des singletons forcés), c'est-à-dire si j'ai une classe appelée DataController, je définis cela dans le même module :

_data_controller = None
def GetDataController():
    global _data_controller
    if _data_controller is None:
        _data_controller = DataController()
    return _data_controller

Ce n’est pas élégant, puisqu’il s’agit de six lignes complètes.Mais tous mes singletons utilisent ce modèle, et il est au moins très explicite (qui est pythonique).

Le Documentation Python couvre cela :

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

Je le réécrirais probablement pour ressembler davantage à ceci :

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

Il devrait être relativement propre d'étendre ceci :

class Bus(Singleton):
    def init(self, label=None, *args, **kwds):
        self.label = label
        self.channels = [Channel("system"), Channel("app")]
        ...

La seule fois où j'ai écrit un singleton en Python, j'ai utilisé une classe où toutes les fonctions membres avaient le décorateur de méthode de classe.

class foo:
  x = 1

  @classmethod
  def increment(cls, y = 1):
    cls.x += y

Il existe également des articles intéressants sur le blog Google Testing, expliquant pourquoi les singletons sont/peuvent être mauvais et constituent un anti-modèle :

Créer un décorateur singleton (c'est-à-dire une annotation) est une manière élégante si vous souhaitez décorer (annoter) des classes à l'avenir.Ensuite, vous mettez simplement @singleton avant la définition de votre classe.

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

@singleton
class MyClass:
    ...

Voici un exemple de Python IAQ de Peter Norvig Comment créer le modèle Singleton en Python ? (Vous devez utiliser la fonction de recherche de votre navigateur pour trouver cette question, il n'y a pas de lien direct, désolé)

Bruce Eckel a également un autre exemple dans son livre Penser en Python (encore une fois, il n'y a pas de lien direct vers le code)

je pense que forcer une classe ou une instance pour être un singleton est exagérée.Personnellement, j'aime définir une classe instanciable normale, une référence semi-privée et une simple fonction d'usine.

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

Ou s'il n'y a aucun problème d'instanciation lors de la première importation du module :

class NothingSpecial:
    pass

THE_ONE_AND_ONLY = NothingSpecial()

De cette façon, vous pouvez écrire des tests sur de nouvelles instances sans effets secondaires, et il n'est pas nécessaire de saupoudrer le module d'instructions globales, et si nécessaire, vous pouvez dériver des variantes à l'avenir.

Le modèle Singleton implémenté avec Python gracieuseté d'ActiveState.

Il semble que l’astuce consiste à placer la classe censée n’avoir qu’une seule instance dans une autre classe.

Étant relativement nouveau sur Python, je ne suis pas sûr de savoir quel est l'idiome le plus courant, mais la chose la plus simple à laquelle je puisse penser est simplement d'utiliser un module au lieu d'une classe.Ce qui aurait été des méthodes d'instance sur votre classe deviennent simplement des fonctions dans le module et toutes les données deviennent simplement des variables dans le module au lieu de membres de la classe.Je soupçonne que c'est l'approche pythonique pour résoudre le type de problème pour lequel les gens utilisent des singletons.

Si vous voulez vraiment une classe singleton, il existe une implémentation raisonnable décrite sur le premier coup sur Google pour "Python singleton", plus précisément :

class Singleton:
    __single = None
    def __init__( self ):
        if Singleton.__single:
            raise Singleton.__single
        Singleton.__single = self

Cela semble faire l'affaire.

OK, un singleton peut être bon ou mauvais, je sais.Il s'agit de mon implémentation, et j'étends simplement une approche classique pour introduire un cache à l'intérieur et produire de nombreuses instances d'un type différent ou plusieurs instances du même type, mais avec des arguments différents.

Je l'ai appelé Singleton_group, car il regroupe des instances similaires et empêche qu'un objet de la même classe, avec les mêmes arguments, puisse être créé :

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

Chaque objet porte le cache singleton...Cela pourrait être mauvais, mais cela fonctionne très bien pour certains :)

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

Ma solution simple qui est basée sur la valeur par défaut des paramètres de fonction.

def getSystemContext(contextObjList=[]):
    if len( contextObjList ) == 0:
        contextObjList.append( Context() )
        pass
    return contextObjList[0]

class Context(object):
    # Anything you want here

Le demi-frère de Singleton

Je suis entièrement d'accord avec Staale et je laisse ici un exemple de création d'un demi-frère singleton :

class void:pass
a = void();
a.__class__ = Singleton

a signalera maintenant comme étant de la même classe que singleton même si cela ne lui ressemble pas.Ainsi, les singletons utilisant des classes compliquées finissent par dépendre du fait que nous ne les manipulons pas beaucoup.

Ainsi, nous pouvons avoir le même effet et utiliser des choses plus simples comme une variable ou un module.Pourtant, si nous voulons utiliser des classes pour plus de clarté et parce que en Python, une classe est un objet, donc nous avons déjà l'objet (pas une instance, mais il fera l'affaire).

class Singleton:
    def __new__(cls): raise AssertionError # Singletons can't have instances

Là, nous avons une belle erreur d'assertion si nous essayons de créer une instance, et nous pouvons stocker sur des dérivations des membres statiques et y apporter des modifications au moment de l'exécution (j'adore Python).Cet objet est aussi bon que d'autres demi-frères (vous pouvez toujours les créer si vous le souhaitez), cependant il aura tendance à fonctionner plus vite en raison de sa simplicité.

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

Dans les cas où vous ne souhaitez pas la solution basée sur les métaclasses ci-dessus et que vous n'aimez pas l'approche simple basée sur un décorateur de fonctions (par ex.car dans ce cas les méthodes statiques sur la classe singleton ne fonctionneront pas), ce compromis fonctionne :

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)
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top