Heim  >  Artikel  >  Backend-Entwicklung  >  Fünf Möglichkeiten, den Singleton-Modus in Python zu implementieren

Fünf Möglichkeiten, den Singleton-Modus in Python zu implementieren

WBOY
WBOYnach vorne
2023-05-26 22:31:241523Durchsuche

Python 实现单例模式的五种写法

Singleton-Muster ist ein häufig verwendetes Software-Designmuster. Der Hauptzweck dieses Musters besteht darin, sicherzustellen, dass nur eine Instanz einer bestimmten Klasse vorhanden ist. Singleton-Objekte sind praktisch, wenn Sie möchten, dass nur eine Instanz einer bestimmten Klasse im gesamten System angezeigt wird.

Zum Beispiel werden die Konfigurationsinformationen eines Serverprogramms in einer Datei gespeichert und der Client liest die Konfigurationsdateiinformationen über eine AppConfig-Klasse. Wenn der Inhalt der Konfigurationsdatei während der Ausführung des Programms an vielen Stellen verwendet werden muss, müssen also an vielen Stellen Instanzen des AppConfig-Objekts erstellt werden, was zur Existenz mehrerer AppConfig-Instanzobjekte führt im System, und dies führt zu einer erheblichen Speicherverschwendung, insbesondere wenn die Konfigurationsdatei viel Inhalt enthält.

Tatsächlich hoffen wir für eine Klasse wie AppConfig, dass während der Ausführung des Programms nur ein Instanzobjekt vorhanden ist.

In Python können wir verschiedene Methoden verwenden, um das Singleton-Muster zu implementieren:

  1. Module verwenden
  2. Dekoratoren verwenden
  3. Klassen verwenden
  4. Basierend auf der __new__-Methode
  5. Basierend auf Metaklasse

Das Folgende ist eine detaillierte Einführung:

Verwenden von Modulen

Tatsächlich handelt es sich bei Python-Modulen um einen natürlichen Singleton-Modus, da das Modul beim ersten Import und beim Import eine .pyc-Datei generiert Beim zweiten Mal wird die .pyc-Datei direkt geladen, ohne dass der Modulcode erneut ausgeführt werden muss.

Wir müssen also nur die relevanten Funktionen und Daten in einem Modul definieren, um ein Singleton-Objekt zu erhalten.

Wenn wir wirklich eine Singleton-Klasse wollen, können wir Folgendes in Betracht ziehen:

class Singleton(object):
 def foo(self):
 pass
singleton = Singleton()

Speichern Sie den obigen Code in der Datei mysingleton.py. Wenn Sie ihn verwenden möchten, importieren Sie die Objekte in dieser Datei direkt in andere Dateien Das Objekt ist das Objekt des Singleton-Modus Das Programm wird ausgeführt und das gedruckte Ergebnis lautet wie folgt:

from mysingleton import singleton

Es scheint, dass es kein Problem gibt. Das liegt daran, dass die Ausführungsgeschwindigkeit zu hoch ist. Wenn die __init__-Methode einige E/A-Vorgänge enthält, treten Probleme auf.


Im Folgenden simulieren wir time.sleep. Wir fügen der obigen __init__-Methode den folgenden Code hinzu:

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)

Nach der erneuten Ausführung des Programms sind die Ergebnisse wie folgt:

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

Das Problem tritt auf! Auf die oben beschriebene Weise erstellte Singletons unterstützen kein Multithreading.

Lösung: Sperren! Der entsperrte Teil wird gleichzeitig ausgeführt und der gesperrte Teil wird seriell ausgeführt, was die Geschwindigkeit verringert, aber die Datensicherheit gewährleistet.

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

Das gedruckte Ergebnis lautet wie folgt:

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

Das ist fast alles, aber es gibt immer noch ein kleines Problem, nämlich wenn das Programm ausgeführt wird, nachdem time.sleep(20) ausgeführt wurde und das Objekt unten instanziiert wird , es ist bereits ein Singleton-Muster.

Aber wir haben immer noch eine Sperre hinzugefügt, was nicht gut ist. Lassen Sie uns etwas optimieren und die Instanzmethode wie folgt ändern:

def __init__(self):
 import time
 time.sleep(1)

Auf diese Weise wird ein Singleton-Modus fertiggestellt, der Multithreading unterstützen kann. +

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

Der auf diese Weise implementierte Singleton-Modus unterliegt Einschränkungen bei der Verwendung. Die spätere Instanziierung muss über obj = Singleton.instance() erfolgen.

Wenn Sie obj = Singleton() verwenden, erhalten Sie auf diese Weise keine Singleton.


Basierend auf der __new__-Methode

Anhand des obigen Beispiels können wir erkennen, dass wir bei der Implementierung eines Singletons intern eine Sperre hinzufügen müssen, um die Thread-Sicherheit zu gewährleisten.


Wir wissen, dass wir beim Instanziieren eines Objekts zuerst die Methode __new__ der Klasse ausführen (wenn wir sie nicht schreiben, wird standardmäßig object.__new__ aufgerufen), um das Objekt zu instanziieren der Klasse wird initialisiert, sodass wir das Singleton-Muster darauf basierend implementieren können.

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)

Das gedruckte Ergebnis lautet wie folgt:

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

Wenn Sie diesen Singleton-Modus verwenden und das Objekt in Zukunft instanziieren, ist die Methode zum Instanziieren des Objekts dieselbe wie üblich obj = Singleton().


Implementiert basierend auf der Metaklassenmethode


Zugehöriges Wissen:

Beim Erstellen einer Klasse wird die Methode __init__ vom Typ automatisch ausgeführt und class() führt die Methode __call__ aus (die __new__-Methode der Klasse, __init__-Methode der Klasse).


Objekte werden von Klassen erstellt. Beim Erstellen eines Objekts wird die __init__-Methode der Klasse automatisch ausgeführt und object() führt die __call__-Methode der Klasse aus.

@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

Verwendung der Metaklasse:

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)

Implementieren des Singleton-Modus:

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

Das obige ist der detaillierte Inhalt vonFünf Möglichkeiten, den Singleton-Modus in Python zu implementieren. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:51cto.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen