Maison >développement back-end >Tutoriel Python >Cinq façons d'implémenter le mode singleton en Python
Singleton Pattern est un modèle de conception de logiciel couramment utilisé. L'objectif principal de ce modèle est de garantir qu'une seule instance d'une certaine classe existe. Les objets Singleton sont utiles lorsque vous souhaitez qu'une seule instance d'une certaine classe apparaisse dans l'ensemble du système.
Par exemple, les informations de configuration d'un programme serveur sont stockées dans un fichier et le client lit les informations du fichier de configuration via une classe AppConfig. Si lors de l'exécution du programme, le contenu du fichier de configuration doit être utilisé à plusieurs endroits, c'est-à-dire que des instances de l'objet AppConfig doivent être créées à plusieurs endroits, cela conduira à l'existence de plusieurs instances AppConfig. objets dans le système, ce qui gaspillera sérieusement les ressources de mémoire, surtout si le fichier de configuration contient beaucoup de contenu.
En fait, pour une classe comme AppConfig, nous espérons qu'un seul objet instance existera lors de l'exécution du programme.
Dans Python, nous pouvons utiliser une variété de méthodes pour implémenter le modèle singleton:
Ce qui suit est une introduction détaillée :
En fait, les modules Python sont en mode singleton naturel, car le module générera un fichier .pyc lors de sa première importation et lors de son importation. pour la deuxième fois, le fichier .pyc sera chargé directement sans réexécuter le code du module.
Il suffit donc de définir les fonctions et les données pertinentes dans un module pour obtenir un objet singleton.
Si nous voulons vraiment une classe singleton, nous pouvons envisager de faire ceci :
class Singleton(object): def foo(self): pass singleton = Singleton()
Enregistrez le code ci-dessus dans le fichier mysingleton.py Lorsque vous souhaitez l'utiliser, importez directement les objets de ce fichier dans d'autres fichiers, This. l'objet est l'objet du mode singleton
from mysingleton import singleton
def Singleton(cls): _instance = {} def _singleton(*args, **kargs): if cls not in _instance: _instance[cls] = cls(*args, **kargs) return _instance[cls] return _singleton @Singleton class A(object): a = 1 def __init__(self, x=0): self.x = x a1 = A(2) a2 = A(3)
class Singleton(object): def __init__(self): pass @classmethod def instance(cls, *args, **kwargs): if not hasattr(Singleton, "_instance"): Singleton._instance = Singleton(*args, **kwargs) return Singleton._instance
En général, tout le monde pense que cela complète le mode singleton, mais il y aura des problèmes lors de l'utilisation de multi-threads :
class Singleton(object): def __init__(self): pass @classmethod def instance(cls, *args, **kwargs): if not hasattr(Singleton, "_instance"): Singleton._instance = Singleton(*args, **kwargs) return Singleton._instance import threading def task(arg): obj = Singleton.instance() print(obj) for i in range(10): t = threading.Thread(target=task,args=[i,]) t.start()
Après le programme est exécuté, le résultat imprimé est le suivant :
<__main__.Singleton object at 0x02C933D0> <__main__.Singleton object at 0x02C933D0> <__main__.Singleton object at 0x02C933D0> <__main__.Singleton object at 0x02C933D0> <__main__.Singleton object at 0x02C933D0> <__main__.Singleton object at 0x02C933D0> <__main__.Singleton object at 0x02C933D0> <__main__.Singleton object at 0x02C933D0> <__main__.Singleton object at 0x02C933D0> <__main__.Singleton object at 0x02C933D0>
Il semble qu'il n'y ait pas de problème. C'est parce que la vitesse d'exécution est trop rapide. S'il y a des opérations d'E/S dans la méthode __init__, des problèmes seront détectés.
Ci-dessous nous simulons via time.sleep. Nous ajoutons le code suivant à la méthode __init__ ci-dessus :
def __init__(self): import time time.sleep(1)
Après avoir réexécuté le programme, les résultats sont les suivants :
<__main__.Singleton object at 0x034A3410> <__main__.Singleton object at 0x034BB990> <__main__.Singleton object at 0x034BB910> <__main__.Singleton object at 0x034ADED0> <__main__.Singleton object at 0x034E6BD0> <__main__.Singleton object at 0x034E6C10> <__main__.Singleton object at 0x034E6B90> <__main__.Singleton object at 0x034BBA30> <__main__.Singleton object at 0x034F6B90> <__main__.Singleton object at 0x034E6A90>
Le problème se pose ! Les singletons créés de la manière ci-dessus ne peuvent pas prendre en charge le multithreading.
Solution : Verrouillez-le ! La partie déverrouillée est exécutée simultanément et la partie verrouillée est exécutée en série, ce qui réduit la vitesse mais garantit la sécurité des données.
import time import threading class Singleton(object): _instance_lock = threading.Lock() def __init__(self): time.sleep(1) @classmethod def instance(cls, *args, **kwargs): with Singleton._instance_lock: if not hasattr(Singleton, "_instance"): Singleton._instance = Singleton(*args, **kwargs) return Singleton._instance def task(arg): obj = Singleton.instance() print(obj) for i in range(10): t = threading.Thread(target=task,args=[i,]) t.start() time.sleep(20) obj = Singleton.instance() print(obj)
Le résultat imprimé est le suivant :
<__main__.Singleton object at 0x02D6B110> <__main__.Singleton object at 0x02D6B110> <__main__.Singleton object at 0x02D6B110> <__main__.Singleton object at 0x02D6B110> <__main__.Singleton object at 0x02D6B110> <__main__.Singleton object at 0x02D6B110> <__main__.Singleton object at 0x02D6B110> <__main__.Singleton object at 0x02D6B110> <__main__.Singleton object at 0x02D6B110> <__main__.Singleton object at 0x02D6B110>
C'est presque ça, mais il y a encore un petit problème, c'est-à-dire lorsque le programme est exécuté, après l'exécution de time.sleep(20), lorsque l'objet est instancié ci-dessous , c'est déjà un modèle singleton.
Mais nous avons quand même ajouté un verrou, ce qui n'est pas bon. Faisons quelques optimisations et modifions la méthode d'instance comme suit :
@classmethod def instance(cls, *args, **kwargs): if not hasattr(Singleton, "_instance"): with Singleton._instance_lock: if not hasattr(Singleton, "_instance"): Singleton._instance = Singleton(*args, **kwargs) return Singleton._instance
De cette façon, un mode singleton pouvant prendre en charge le multi-threading est terminé. +
import time import threading class Singleton(object): _instance_lock = threading.Lock() def __init__(self): time.sleep(1) @classmethod def instance(cls, *args, **kwargs): if not hasattr(Singleton, "_instance"): with Singleton._instance_lock: if not hasattr(Singleton, "_instance"): Singleton._instance = Singleton(*args, **kwargs) return Singleton._instance def task(arg): obj = Singleton.instance() print(obj) for i in range(10): t = threading.Thread(target=task,args=[i,]) t.start() time.sleep(20) obj = Singleton.instance() print(obj)
Le mode singleton implémenté de cette manière a des restrictions d'utilisation. L'instanciation ultérieure doit être effectuée via obj = Singleton.instance()
Si vous utilisez obj = Singleton(), ce que vous obtenez de cette manière n'est pas un célibataire.
Grâce à l'exemple ci-dessus, nous pouvons savoir que lorsque nous implémentons un singleton, nous devons ajouter un verrou interne pour garantir la sécurité des threads.
Nous savons que lorsque nous instancions un objet, nous exécutons d'abord la méthode __new__ de la classe (quand nous ne l'écrivons pas, l'objet.__new__ est appelé par défaut) pour instancier l'objet puis exécutons la méthode __init__ ; de la classe. Cet objet est initialisé, nous pouvons donc implémenter le modèle singleton basé sur cela.
import threading class Singleton(object): _instance_lock = threading.Lock() def __init__(self): pass def __new__(cls, *args, **kwargs): if not hasattr(Singleton, "_instance"): with Singleton._instance_lock: if not hasattr(Singleton, "_instance"): Singleton._instance = object.__new__(cls) return Singleton._instance obj1 = Singleton() obj2 = Singleton() print(obj1,obj2) def task(arg): obj = Singleton() print(obj) for i in range(10): t = threading.Thread(target=task,args=[i,]) t.start()
Le résultat imprimé est le suivant :
<__main__.Singleton object at 0x038B33D0> <__main__.Singleton object at 0x038B33D0> <__main__.Singleton object at 0x038B33D0> <__main__.Singleton object at 0x038B33D0> <__main__.Singleton object at 0x038B33D0> <__main__.Singleton object at 0x038B33D0> <__main__.Singleton object at 0x038B33D0> <__main__.Singleton object at 0x038B33D0> <__main__.Singleton object at 0x038B33D0> <__main__.Singleton object at 0x038B33D0> <__main__.Singleton object at 0x038B33D0> <__main__.Singleton object at 0x038B33D0>
En utilisant ce mode singleton, lors de l'instanciation de l'objet dans le futur, la méthode d'instanciation de l'objet est la même que d'habitude obj = Singleton().
Connaissances connexes :
例子: class Foo: def __init__(self): pass def __call__(self, *args, **kwargs): pass obj = Foo() # 执行type的 __call__ 方法,调用 Foo类(是type的对象)的 __new__方法,用于创建对象,然后调用 Foo类(是type的对象)的 __init__方法,用于对对象初始化。 obj()# 执行Foo的 __call__ 方法
Utilisation de la métaclasse :
class SingletonType(type): def __init__(self,*args,**kwargs): super(SingletonType,self).__init__(*args,**kwargs) def __call__(cls, *args, **kwargs): # 这里的cls,即Foo类 print('cls',cls) obj = cls.__new__(cls,*args, **kwargs) cls.__init__(obj,*args, **kwargs) # Foo.__init__(obj) return obj class Foo(metaclass=SingletonType): # 指定创建Foo的type为SingletonType def __init__(self,name): self.name = name def __new__(cls, *args, **kwargs): return object.__new__(cls) obj = Foo('xx')
Implémentation du mode singleton :
import threading class SingletonType(type): _instance_lock = threading.Lock() def __call__(cls, *args, **kwargs): if not hasattr(cls, "_instance"): with SingletonType._instance_lock: if not hasattr(cls, "_instance"): cls._instance = super(SingletonType,cls).__call__(*args, **kwargs) return cls._instance class Foo(metaclass=SingletonType): def __init__(self,name): self.name = name obj1 = Foo('name') obj2 = Foo('name') print(obj1,obj2)
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!