Python中实现单例的几种方式

Python如何实现单例?

什么是单例模式?

单例模式:一个类只能有一个实例化对象存在的模式。

如何实现单例?

1.使用模块

python中模块是天然的单例模式,当一个模块被调用时,会生成对应的.pyc文件,接下来的每次使用都会自动读取.pyc文件里的内容,因此,要使用模块实现单例,只需这样做:

# mysingleton.py
class Singleton:
    def fuc(self):
        pass
singleton = Singleton()

我们在使用时只需引用该模块的singleton对象就行了

2.使用装饰器
def singleton(cls):
    _instance = {}
    def _singleton(*args, **kwargs):
        if cls not in _instance:
            _instance[cls] = cls(*args, **kwargs)
		return _instance[cls]
    return _singleton

@singleton
class A:
    a = 1 # 类属性
    def __init__(self, x):
        self.x = x # 类的实例化对象属性
a1 = A(1)
a2 = A(2)
3.使用类
class Singleton:
    def __init__(self, *args, **kwargs):
        pass

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

上述方式实现的单例模式,在单线程时是可行的,但是在多线程时就会出现问题,假设上述的__init__()方法中有一些耗时的io操作,这里我们用sleep模拟一下:

class Singleton:
    def __init__(self, *args, **kwargs):
        import time
        time.sleep(10)

再用多线程执行创建实例化对象的操作:

import threading
def task(arg):
    job = Singleton.instance()
    print(job)
for i in range(10):
    t = threading.Thread(task,args=[i,])
    t.start()

执行结果如下

<__main__.Singleton object at 0x7ff873a9c2d0>
<__main__.Singleton object at 0x7ff873b23e10>
<__main__.Singleton object at 0x7ff873b30850>
<__main__.Singleton object at 0x7ff873b30350>
<__main__.Singleton object at 0x7ff873b30710>
<__main__.Singleton object at 0x7ff873b23f50>
<__main__.Singleton object at 0x7ff873b305d0>
<__main__.Singleton object at 0x7ff873b30490>
<__main__.Singleton object at 0x7ff873b30210>
<__main__.Singleton object at 0x7ff873b300d0>

从执行结果中我们可以发现:在多线程情况之下,通过上述方式创建的实例化对象,内存地址并不一样,这也就意味着我们并没有实现单例模式。那么,为什么这种单例模式的写法无法在多线程情况下奏效呢?
在搞清楚这个问题之前,我们需要先了解一点,即:任何数据实例化过程或者数据声明过程都是向系统申请内存空间,这些过程都是io,也即耗时操作。
再回到代码本身,在多线程情况下,当线程1运行完if not,但还没有执行实例化过程,此时如果CPU切换到线程2,线程2也执行到了if not,并完成了实例化过程,然后CPU再切换回了线程1,并继续执行了线程1的实例化过程,那么就是两个线程分别实例化了一次,也就不是单例了(这里还需要了解CPU的切换机制,在此处就不详细阐述了)
搞清楚上述方法无法满足多线程的原因之后,我们再来看如何去解决上述问题:
添加线程锁

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

    @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

当我们添加了线程锁之后,就可以在多线程情况下实现单例了:

<__main__.Singleton object at 0x7f834719c310>
<__main__.Singleton object at 0x7f834719c310>
<__main__.Singleton object at 0x7f834719c310>
<__main__.Singleton object at 0x7f834719c310>
<__main__.Singleton object at 0x7f834719c310>
<__main__.Singleton object at 0x7f834719c310>
<__main__.Singleton object at 0x7f834719c310>
<__main__.Singleton object at 0x7f834719c310>
<__main__.Singleton object at 0x7f834719c310>
<__main__.Singleton object at 0x7f834719c310>
4.基于__new__方法实现(推荐)

实例化对象时,会先执行类的new方法(没有__new__方法时会默认执行object的__new__方法),然后再执行__init__方法初始化实例对象,那么我们可以通过定义__new__方法实现单例

import threading
class Singleton:
    _instance_lock = threading.Lock()
    def __init__(self, *args, **kwargs):
        pass
    def __new__(cls, *args, **kwargs):
        if not hasattr(Singleton, "_instance"):
            with _instance_lock:
                if not hasattr(Singleton, "_instance"):
                    Singleton._instance = object.__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()

执行结果:

<__main__.Singleton object at 0x7f9f37a9c350>
<__main__.Singleton object at 0x7f9f37a9c350>
<__main__.Singleton object at 0x7f9f37a9c350>
<__main__.Singleton object at 0x7f9f37a9c350>
<__main__.Singleton object at 0x7f9f37a9c350>
<__main__.Singleton object at 0x7f9f37a9c350>
<__main__.Singleton object at 0x7f9f37a9c350>
<__main__.Singleton object at 0x7f9f37a9c350>
<__main__.Singleton object at 0x7f9f37a9c350>
<__main__.Singleton object at 0x7f9f37a9c350>
5.基于metaclass(元类)实现
"""
关于元类(type):这里元类的概念,我们可以理解为创建类的类。当我们创建一个类时,会执行type的__init__()方法,使用类名+()创建类的实例化对像时,会调用元类type的__call__()方法。
关于__call__():该方法的功能类似于在类中重载()运算符,使得类的实例化对象可以像调用普通函数那样,以对象名+()的形式调用,普通类的对象基于类,类基于元类,都是如此。
"""

__call__的使用:

class Foo:
    def __call__(self, *args, **kwargs):
        print("执行__call__。。。")

obj = Foo()
print("创建实例化对象obj")
obj()

输出结果为:

创建实例化对象obj
执行__call__。。。

利用元类的__call__方法,我们可以这样实现单例:

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):
        import time
        time.sleep(5)

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

for i in range(10):
    t = threading.Thread(target=task, args=[i,])
    t.start()

执行结果为:

<__main__.Foo object at 0x7ff725a9cf50>
<__main__.Foo object at 0x7ff725a9cf50>
<__main__.Foo object at 0x7ff725a9cf50>
<__main__.Foo object at 0x7ff725a9cf50>
<__main__.Foo object at 0x7ff725a9cf50>
<__main__.Foo object at 0x7ff725a9cf50>
<__main__.Foo object at 0x7ff725a9cf50>
<__main__.Foo object at 0x7ff725a9cf50>
<__main__.Foo object at 0x7ff725a9cf50>
<__main__.Foo object at 0x7ff725a9cf50>

参考链接https://www.cnblogs.com/huchong/p/8244279.html

posted @ 2021-03-01 15:48  汪汪队大队长  阅读(593)  评论(0编辑  收藏  举报