Skip to content

设计模式之单例模式

生命不止,奋斗不息。

Author: 李东阳

1、什么是单例模式

单例模式,是一种常用的软件设计模式。使用它就是为了保证在全局环境下只能有一个该类的实例对象

通过单例模式可以保证系统中一个类只有一个实例化对象并且这个实例对象是易于外界访问的,控制实例对象的个数有利于节约系统资源。

单例模式的应用场景:就比如说太极魔盒这个项目中的mediapipe视频检测进程类,这个项目中就有多个地方需要用到这个进程类。但是我们知道,进程的创建和销毁是相当消耗系统资源的,更何况是创建多个这样的进程类。所以,这里我们就可以把这个mediapipe进程类写成一个单例模式。

2、单例模式之____new____实现

我们知道,Python中,一个类的创建实例对象是通过调用祖宗类object的 __new__(cls)方法来实现的。所以我们可以通过重写 __new__(cls)方法去实现单例模式。下面是具体代码:

class Bar(object):
    __instance = None  # 可以理解为一个这个类是否创建过对象的标记位
    # 重写__new__ 方法
    def __new__(cls, *args, **kwargs):  # 创建一个类的实例对象就是在调用object的__new__方法
        if cls.__instance == None:  # 判断是否已经创建过实例对象
            cls.__instance == object.__new__(cls)  # 没有就返回一个新创建的对象
            return cls.__instance
        else:  # 否则返回之前已经创建好的实例对象
            return cls.__instance

a = Bar()     # 这里实例化了两个类对象,但是可以看到内存地址是相同的,说明其实这两个对象是同一个
b = Bar()
print(id(a))        # 1659937936
print(id(b))        # 1659937936

3、单例模式之装饰器实现

def singleton(cls):
    __instance = {}
    def wrapper(*args, **kwargs):
        if cls not in __instance:
            __instance[cls] = cls(*args, **kwargs)
            return __instance[cls]
        else:
            return __instance[cls]
    return wrapper

@singleton
class Bar:
    pass

a = Bar()
b = Bar()
print(id(a))        # 2129789898368
print(id(b))        # 2129789898368

4、单例模式之import实现

这个方法比较直观,我们先在一个py文件中写好这个类,然后实例化出一个对象出来。当我要用这个类的实例对象时,我们只需要在这个py文件中把这个实例化对象导入即可。

c1.py

class My_Singleton(object):
    def foo(self):
        pass

    def bar(self):
        pass

my_singleton = My_Singleton()

c2.py

from c1 import my_singleton

a = my_singleton.foo()
b = my_singleton.bar()
print(id(a))        # 1659937936
print(id(b))        # 1659937936

5、单例模式之共享属性实现

创建实例时把所有实例对象的属性字典:__dict__指向同一个字典,这样它们具有相同的属性和方法。

6、单例模式的优缺点

优点

  • 实例控制:单例模式会阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一实例。
  • 灵活性:因为类控制了实例化过程,所以类可以灵活更改实例化过程。

缺点

  • ​ 开销:虽然数量很少,但如果每次对象请求引用时都要检查是否存在类的实例,将仍然需要一些开销。可以通过使用静态初始化解决此问题。

  • ​ 可能的开发混淆:使用单例对象(尤其在类库中定义的对象时,开发人员必须记住自己不能使用 new 关键字实例化对象。因为可能无法访问库源代码,因此应用程序开发人员可能会意外发现自己无法直接实例化此类。

  • ​ 对象生成期:不能解决删除单个对象的问题。