Maison  >  Article  >  développement back-end  >  Cinq façons d'implémenter le mode singleton en Python

Cinq façons d'implémenter le mode singleton en Python

WBOY
WBOYavant
2023-05-26 22:31:241475parcourir

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:

  1. Utilisation de modules
  2. Utilisation des décorateurs
  3. Utilisation de classes
  4. basée sur la méthode __New__
  5. basée sur la métaclasse

Ce qui suit est une introduction détaillée :

Utilisation des modules

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

Utilisation de décorateurs

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)

Utilisation de classes

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.

Basé sur la méthode __new__

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

Implémenté basé sur la méthode métaclasse

Connaissances connexes :

  1. Une classe est créée par type Lorsqu'une classe est créée, la méthode __init__ de type est automatiquement exécutée et la classe() exécute la méthode __call__ de. type (la méthode __new__ de la classe, la méthode __init__ de la classe).
  2. Les objets sont créés par les classes. Lors de la création d'un objet, la méthode __init__ de la classe est automatiquement exécutée et l'objet() exécute la méthode __call__ de la classe.
例子:
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!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer