Python 使用装饰器快速实现单例模式

1. 本文地址

  1. 博客园:https://www.cnblogs.com/coco56/p/11253656.html
  2. 简书:https://www.jianshu.com/p/4c47f8e3809b
  3. CSDN:https://blog.csdn.net/COCO56/article/details/97409050

2. 对象的作用及为何只要一个对象

众所周知,对象是解决继承的问题的,如果没有对象,那么将很难解决继承的问题。这点儿和人类的现实世界有相似之处,没有对象的话何谈子嗣的继承问题?
没有对象,将意味着很难实现继承时的多态、很难去塑造子类,没有子类又将意味着父类在设计时要具备所有的功能,这绝对是极不现实的。一种很现实的做法是:父类解决一些问题,子类再解决一些问题,然后子类的子类再解决一些问题,这样子子孙孙无穷尽也,一定能够把问题解决好的,这种设计模式也一定能够应对复杂多变的现实环境,因此对象的存在意义重大。
但对象是越多越好吗?答案绝对是否定的,很多情况下我们只需要一个对象就可以了,多余的对象会带来很多的麻烦。这是因为每个对象都要占据一定的内存空间和CPU算力,大多数情况下我们只需要一个对象去执行任务,对于一颗CPU的核心而言,操作一个对象是最快的,为什么?这主要是由于线程的切换会造成不必要的开销。
设想一下,假如你有一个对象可以用来接吻,你的DNA约定你一生要完成4800次接吻才算完成了任务,这样在你临死的时候才会死而无憾,即程序完成所有任务后正常退出,返回值为0,代表剩余待做的任务数为0。平均你和一个对象每天的最多能接吻48次,这样你100天就完成了任务。但如果你有多个对象的话,你的平均作战能力并不会提升,这是因为48次是你的处理极限,相反,你因为要和多个对象进行接吻,每次从一个对象移动到另一个对象那里会产生不必要的路程开销,会大大影响你的工作效率。假设你有两个对象,对象A在河南,对象B在河北,你从河南到河北需要一天的时间,你每天工作完了之后会切换到另一个对象那里。假设先从与对象A接吻开始,工作一天共接吻48次,但你第二天需要移动到对象B那里,移动的过程中是没有对象可以接吻的,只有到达目的地之后才能进入工作状态。你切换任务的过程的时间将消耗一天,这一天等于白白浪费了。这样的话,你是一天干活,一天用于切换任务,即从当前对象跑到另一个对象那里,平均下来,两天只能完成48次的接吻任务,这样的话你需要花费200天才能完成DNA上面约定的接吻任务量,效率比着单对象模式大打折扣。
在计算机中,类的对象又称为类的实例,因此我们把一个类只生成一个对象的模式称为单例模式

以下是常见的几种创建单例的模式。
说明:我写的懒汉式与饿汉式和别人的命名刚好是相反的,这个感觉每个人的理解不同,叫什么名字无所谓啦,只要能理解思想就行。

3. 懒汉式

懒汉式就是说“国家”分配对象,在你还未出生的时候就已经被指腹为婚了,这样在你出生的时候就立即拥有了一个对象了,再也不用发愁对象的事儿了,这种不需要自己主动付出就能得到对象的模式被称为懒汉模式。
为了方便,这里我是用装饰器,对需要的类进行装饰,达到了一次定义,以后再处处使用时只需要一行代码的效果。
优点:省心,开始的时候一人分一个对象就好了,很省事。
缺点:构造函数只能是无参的,自己有想法,想传个参数的话是不好传过去的。这是因为在设计时就没考虑你有想法的情况下,别管三七二十一,上来就给你分一个对象。

def Singleton(cls):
    print("正在进行装饰,在类的上方用个@符at一下我就可以了哦")
    cls._instance = cls()#需要传参数的话在这里改一下参数列表,但那样的话就不具备如此广泛的通用性了,我们想要的效果是一次定义装饰器,以后对所有的类都适用。
    def _singleton(): return cls._instance
    return _singleton

@Singleton
class A(object):
    @classmethod
    def __new__(cls, *args, **kwargs):
        print('__new__')
        return super().__new__(cls)

    def __init__(self, *args, **kwargs):
        print('__init__')
    

@Singleton
class B(object):
    @classmethod
    def __new__(cls, *args, **kwargs):
        print('__new__')
        return super().__new__(cls)

    def __init__(self, *args, **kwargs):
        print('__init__')


if __name__ == '__main__':
    a1 = A(); print(a1)
    a2 = A(); print(a2)
    print()

    b1 = B(); print(b1)

4. 饿汉式

饿汉式是指你饿了才给你分一个对象,把对象只分配给那些饥渴难耐大汉们(不分的话可能会出现问题,毕竟大汉如果发起情来还是很骚的,恐怕难以招架)。
优点:节省空间,物尽其用,需要时才给你,如果不需要想单身一辈子的话就不给你分配对象了。
缺点:可能存在线程不安全,饥渴难耐的大汉如果在分配对象的时候一下子多占了多个不同的对象怎么办?

4.1. 未加锁版

一般在初始化对象的时候如果不进行IO操作,是没事儿的。(即__init__方法里没有IO操作)
这个版本实现简单,执行速度也快,不用来回上锁了。加锁版的就是给大汉分对象的时候先把大汉五花大绑地锁起来,这样的话就避免了在分配时他抢占多个对象的可能。分配完了之后再给大汉松下绑、解下锁,这样步骤一多,肯定是比较耗时的。
示例1:装饰时给每个类创建一个_instance属性

def Singleton(cls):
    print("正在进行装饰,在类的上方用个@符at一下我就可以了哦")
    cls._instance = None

    def _singleton(*args, **kargs):
        if cls._instance: return cls._instance
        cls._instance = cls(*args, **kargs)
        return cls._instance
    
    return _singleton

@Singleton
class A(object):
    @classmethod
    def __new__(cls, *args, **kwargs):
        print('__new__')
        return super().__new__(cls)

    def __init__(self, *args, **kwargs):
        print('__init__')
    

@Singleton
class B(object):
    @classmethod
    def __new__(cls, *args, **kwargs):
        print('__new__')
        return super().__new__(cls)

    def __init__(self, *args, **kwargs):
        print('__init__')


if __name__ == '__main__':
    a1 = A(); print(a1)
    a2 = A(); print(a2)
    print()

    b1 = B(); print(b1)

示例2:装饰给创建一个空字典,生成对象时再把这个对象添加到字典里,下次如果查到字典里有所需对象的话就直接返回了,没有的话创建后再返回。

def Singleton(cls):
    print("正在进行装饰,在类的上方用个@符at一下我就可以了哦")

    _instance = {}

    def _singleton(*args, **kargs):
        if cls in _instance: return _instance[cls]
        _instance[cls] = cls(*args, **kargs)
        return _instance[cls]
    
    return _singleton

@Singleton
class A(object):
    @classmethod
    def __new__(cls, *args, **kwargs):
        print('__new__')
        return super().__new__(cls)

    def __init__(self, *args, **kwargs):
        print('__init__')
    

@Singleton
class B(object):
    @classmethod
    def __new__(cls, *args, **kwargs):
        print('__new__')
        return super().__new__(cls)

    def __init__(self, *args, **kwargs):
        print('__init__')


if __name__ == '__main__':
    a1 = A(); print(a1)
    a2 = A(); print(a2)
    print()

    b1 = B(); print(b1)

4.2. 加锁版

import threading

def Singleton(cls):
    print("正在进行装饰,在类的上方用个@符at一下我就可以了哦")

    cls._lock = threading.Lock()
    cls._instance = None

    def _singleton(*args, **kargs):
        if cls._instance: return cls._instance
        cls._lock.acquire()#上锁
        cls._instance = cls(*args, **kargs)
        cls._lock.release()#去锁
        return cls._instance
    
    return _singleton

@Singleton
class A(object):
    @classmethod
    def __new__(cls, *args, **kwargs):
        print('__new__')
        return super().__new__(cls)

    def __init__(self, *args, **kwargs):
        print('__init__')
    

@Singleton
class B(object):
    @classmethod
    def __new__(cls, *args, **kwargs):
        print('__new__')
        return super().__new__(cls)

    def __init__(self, *args, **kwargs):
        print('__init__')


if __name__ == '__main__':
    a1 = A(); print(a1)
    a2 = A(); print(a2)
    print()

    b1 = B(); print(b1)

猜你喜欢

转载自www.cnblogs.com/coco56/p/11253656.html