当前位置:网站首页>Five design schemes of singleton mode

Five design schemes of singleton mode

2020-11-08 19:00:00 Python- talk

python The singleton pattern of design pattern

Singleton pattern is a common development and design pattern , Its main purpose is to ensure that only one instance object exists , That is to say, when you want a class to have a single function , You only need an instance object to complete when needed , You can use singleton mode , To save memory resources .

Let's say we have web Under development , One of the functions we often need to do is to use singleton mode to develop SMS captcha . We usually use communication products to send SMS captcha , Then we only need to use an instance object to complete the function of sending SMS .

1. Module implements singleton mode

Everyone should know , Module import can only make the imported program execute once , If you import multiple times, you will only execute once , So we can say that a module is a natural singleton pattern , therefore , We just need to define the relevant functions and data in a module , When you use it, import it to other modules to achieve the effect of singleton mode .

single.py
···
class Singleton(object):
    def foo(self):
        pass
    print(1)
singleton = Singleton()

from singleton import singleton
from singleton import singleton
 Execution results :
1 

2. Using decorators to implement singleton patterns

We all know the function of decorators , We can add a function to other functions or classes , So we can also write a logic to the class , Let the class generate only one instance object .

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)
print(a1)
print(a2)
 Execution results :
<__main__.A object at 0x0000027432DABE48>
<__main__.A object at 0x0000027432DABE48> 

3. Using class methods to implement singleton patterns

When we use class to create instance objects directly , It's not a singleton created , Then we need to define a class method in the class to edit the logic and implement the singleton pattern , The main idea is to judge whether a class has _instance This attribute , If so, this instance is returned directly , If not, create an instance .

class Singleton(object):
    def __init__(self,*args,**kwargs):
        pass

    @classmethod
    def get_instance(cls, *args, **kwargs):
        if not hasattr(Singleton, '_instance'):
            Singleton._instance = Singleton(*args, **kwargs)
        return Singleton._instance

s1 = Singleton.get_instance()
s2 = Singleton.get_instance()
print(s1)
print(s2)
 Execution results :
<__main__.Singleton object at 0x0000017B0359B550>
<__main__.Singleton object at 0x0000017B0359B550> 

However, there is a hidden danger in implementing singleton mode in this way , When we add multithreading to create instance objects , We are fast enough to have no impact , When the execution speed is not fast enough , A thread creates an instance and gets _instance This property is used to judge , Other threads might get this at the same time _instance This attribute , It is found that no instance exists , So these two threads will create an instance at the same time , It will cause the phenomenon that multiple instances are created , Our singleton mode naturally fails . Give an example of , Let's add this break time here to demonstrate the slow execution .

import threading
import time

class Singleton(object):
    def __init__(self, *args, **kwargs):
        time.sleep(1)

    @classmethod
    def get_instance(cls, *args, **kwargs):
        if not hasattr(Singleton, '_instance'):
            Singleton._instance = Singleton(*args, **kwargs)

        return Singleton._instance

def task(arg):
    obj = Singleton.get_instance(arg)
    print(obj)

for i in range(10):
    t = threading.Thread(target=task, args=[i, ])
    t.start()
 Execution results :
<__main__.Singleton object at 0x000001774942A518>
<__main__.Singleton object at 0x00000177495FA160>
<__main__.Singleton object at 0x00000177495F1EF0>
<__main__.Singleton object at 0x00000177495E5940>
<__main__.Singleton object at 0x00000177495AC0F0>
<__main__.Singleton object at 0x00000177495E58D0>
<__main__.Singleton object at 0x00000177495A6FD0>
<__main__.Singleton object at 0x00000177495F1D68>
<__main__.Singleton object at 0x00000177496060B8>
<__main__.Singleton object at 0x0000017749606240> 

Now we can see that the instance object has changed to a different one ip 了 ? This is the reason we just mentioned . So how can we solve this problem ? Here we can achieve the effect by adding thread lock . After we've added thread locks , Here it is _instance Add this lock , Let each county run out before releasing it , Then the next thread can get it and continue running the program , It will not create the situation that different instances are created directly by getting the attribute value at the same time .

import threading
import time

class Singleton(object):
    _instance_lock = threading.Lock()
    def __init__(self, *args, **kwargs):
        time.sleep(1)

    @classmethod
    def get_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.get_instance(arg)
    print(obj)

for i in range(10):
    t = threading.Thread(target=task, args=[i, ])
    t.start()
 Execution results :
<__main__.Singleton object at 0x000001EC8FAB9550>
<__main__.Singleton object at 0x000001EC8FAB9550>
<__main__.Singleton object at 0x000001EC8FAB9550>
<__main__.Singleton object at 0x000001EC8FAB9550>
<__main__.Singleton object at 0x000001EC8FAB9550>
<__main__.Singleton object at 0x000001EC8FAB9550>
<__main__.Singleton object at 0x000001EC8FAB9550>
<__main__.Singleton object at 0x000001EC8FAB9550>
<__main__.Singleton object at 0x000001EC8FAB9550>
<__main__.Singleton object at 0x000001EC8FAB9550> 

4. Use __new__ Method to implement the singleton pattern

We should all know that our instance object is through __new__ Method , If we override class __new__ Method , Can we directly rewrite the case where multiple instances can be created to create only one instance object ? The answer is yes , We can rewrite __new__ Method time , Add judgment if there is an instance object , Then directly return the created object , So we can achieve this effect .

import threading

class Singleton(object):
    _instance_lock = threading.Lock()

    def __init__(self, *args, **kwargs):
        pass

    def __new__(cls, *args, **kwargs):
        if not hasattr(cls, '_instance'):
            with Singleton._instance_lock:
                if not hasattr(cls, '_instance'):
                    Singleton._instance = super().__new__(cls)

            return Singleton._instance

def task(arg):
    obj = Singleton()
    print(obj)

for i in range(10):
    t = threading.Thread(target=task, args=[i, ])
    t.start()
 Running results :
<__main__.Singleton object at 0x00000111C88B9588>
None
None
None
None
None
None
None
None
None 

5. Use metaclass metaclass Implement singleton mode

Python The metaclass of is the class that controls the creation of the class , In that case , Then we can create our class through metaclass when creating class , It can only generate one instance object ?

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(id(obj1))
print(id(obj2))
 Execution results :
<__main__.Foo object at 0x000001E97FC14400>
<__main__.Foo object at 0x000001E97FC14400> 

Okay , So here are some ways to create singleton patterns , in general , It doesn't matter whether it's useful or not , During the interview , We pretend x It still works , you get Have we arrived ?

版权声明
本文为[Python- talk]所创,转载请带上原文链接,感谢