Bien que les avantages et les inconvénients du modèle de conception singleton ne soient pas au centre de cet article, cet article explorera comment pour implémenter le singleton en Python de la meilleure façon possible. Implémentez ce modèle de manière pythonique. Ici, « le plus pythonique » signifie suivre le « principe de la moindre surprise ».
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 :
Inconvénients :
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 :
Inconvénients :
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 :
Inconvénients :
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 :
Inconvénients :
Module Singleton singleton.py.
Avantages :
Inconvénients :
Je recommande d'utiliser la Méthode 2, mais il est préférable d'utiliser des métaclasses au lieu de classes de base. Voici un exemple d'implémentation :
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 que __init__ s'exécute à chaque fois qu'une classe est appelée, ajoutez le code suivant à Singleton.__call__ In l'instruction if :
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
Une métaclasse est une classe de classes, c'est-à-dire qu'une classe est une instance de sa métaclasse. La métaclasse d'un objet en Python peut être trouvée via type(obj). Les nouvelles classes normales sont de type type. Le Logger ci-dessus sera de type class 'your_module.Singleton', tout comme la (seule) instance de Logger sera de type class 'your_module.Logger' . Lorsqu'un enregistreur est appelé à l'aide de Logger(), Python demande d'abord à la métaclasse Singleton du Logger ce qu'il doit faire, permettant la création d'instances préemptives. Le processus est similaire à la façon dont Python demande à une classe ce qu'elle doit faire avec ses attributs en appelant __getattr__, et vous référencez ses attributs en faisant myclass.attribute.
Les métaclasses déterminent essentiellement ce que signifie la classe appelante et comment implémenter cette signification. Voir par exemple http://code.activestate.com/recipes/498149/ qui utilise des métaclasses pour recréer essentiellement des structures de style C en Python. Sujet de discussion [Quels sont les cas d'utilisation spécifiques des métaclasses ? ](https://codereview.stackexchange.com/questions/82786/what-are-some-concrete-use-cases-for-metaclasses) fournit également quelques exemples, qui sont généralement liés à la programmation déclarative, en particulier dans l'ORM utilisé dans .
Dans ce cas, si vous utilisez votre Méthode 2 et qu'une sous-classe définit une méthode __new__, elle sera exécutée à chaque fois que SubClassOfSingleton() est appelée, car elle est responsable de l'appel des méthodes qui renvoyer les instances stockées. Avec les métaclasses, elle n'est exécutée qu'une seule fois, lors de la création de l'instance unique. Vous devez personnaliser la définition de la classe appelante, qui est déterminée par son type.
En général, il est logique d'utiliser des métaclasses pour implémenter des singletons. Un singleton est spécial car son instance n'est créée qu'une seule fois, tandis qu'une métaclasse est une implémentation personnalisée d'une classe créée qui la fait se comporter différemment d'une classe normale. L'utilisation d'une métaclasse vous donne plus de contrôle alors que vous auriez autrement besoin de personnaliser la définition de votre classe singleton.
Votre singleton n'a pas besoin d'héritage multiple (car la métaclasse n'est pas une classe de base), mais pour que l'héritage crée une sous-classe d'une classe, vous devez vous assurer que le singleton la classe est la première/La métaclasse la plus à gauche redéfinit __call__. Il est peu probable que cela pose un problème. Le dictionnaire d'instance ne se trouve pas dans l'espace de noms de l'instance, il ne peut donc pas être écrasé accidentellement.
Vous entendrez également que le modèle singleton viole le « principe de responsabilité unique », ce qui signifie que chaque classe ne doit faire qu'une seule chose. De cette façon, vous n'avez pas à vous soucier de casser une chose que fait le code lorsque vous devez en modifier un autre, car ils sont indépendants et encapsulés. L'implémentation de la métaclasse réussit ce test. Les métaclasses sont chargées d'appliquer le modèle, en créant des classes et des sous-classes qui n'ont pas besoin de savoir qu'elles sont des singletons. La méthode 1 échoue à ce test, comme vous l'avez souligné avec "MyClass elle-même est une fonction, pas une classe, donc les méthodes de classe ne peuvent pas être appelées".
Écrire du code en Python 2 et 3 nécessite un schéma légèrement plus compliqué. Étant donné que les métaclasses sont généralement des sous-classes de la classe de type, vous pouvez utiliser une métaclasse pour créer dynamiquement une classe de base intermédiaire avec elle comme métaclasse au moment de l'exécution, puis utiliser cette classe de base comme classe de base pour une classe de base singleton publique. C'est plus facile à dire qu'à faire, comme suit :
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
L'ironie de cette approche est qu'elle utilise des sous-classes pour implémenter des métaclasses. Un avantage possible est que, contrairement à une métaclasse pure, isinstance(inst, Singleton) renverra True.
Concernant un autre sujet, vous l'avez peut-être remarqué, mais l'implémentation de la classe de base dans votre message d'origine était erronée. Pour référencer des _instances dans une classe, vous devez utiliser super() ou une méthode statique de la méthode de classe, car la classe réelle n'a pas encore été créée au moment de l'appel. Tout cela est également vrai pour les implémentations de métaclasses.
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
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!