单实例模式


# 单实例模式

# 什么是单实例模式

概念

  • 一种常用的软件设计模式
  • 主要是确保一个类只有一个实例存在

例如:

class A:
    pass

a1 = A()
a2 = A()
a3 = A()
print(id(a1))
print(id(a2))
print(id(a3))

结果:
2800308103648
2800308103456
2800308103552
1
2
3
4
5
6
7
8
9
10
11
12
13
14

上述代码中,实例化了 3次 A 这个对象,每个实例的地址不同,显然这不是单实例模式

# 为什么要使用单例模式

举个例子

  • 某个服务器程序的配置信息存放在一个文件中,客户端通过一个 AppConfig 的类来读取配置文件的信息
  • 在程序运行期间,有很多地方都需要使用配置文件的内容,很多地方都需要创建 AppConfig 对象的实例,这就导致系统中存在多个 AppConfig 的实例对象,而这样会严重浪费内存资源,尤其是在配置文件内容很多的情况下
  • 类似 AppConfig 这样的类,我们希望在程序运行期间只存在一个实例对象

作用:防止一个全局使用的类频繁地创建与销毁

# 如何实现单实例模式

# 方式一:将实例赋值给变量

这是最简单实现单实例的方式

修改下上面的代码:

class A:
    pass

a = A()

a1 = a
a2 = a
a3 = a
print(id(a1))
print(id(a2))
print(id(a3))

结果:
1546662907040
1546662907040
1546662907040
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

结果发现 3 个实例的地址相同

# 方式二:通过类的 __new__ 方法

这种方式比较常用,实现很方便,推荐使用

原理

  • 类在实例化时,会先调用 __new__ 方法创建实例,然后对实例进行 __init__ 初始化
  • 如果类没有指定 __new__ 方法,就会调用 Object 的
  • 通过重写 __new__ 方法,判断是否有实例存在,没有则创建,有则返回实例对象,从而实现单例模式
  • __new__ 方法是个类方法
class A:
    def __new__(cls, *args, **kwargs):
        print('__new__ is call')
        if not hasattr(cls,"_instance"):
            cls._instance = super().__new__(cls)
        return cls._instance

    def __init__(self):
        print('__init__ is call')

a1 = A()
a2 = A()
a3 = A()
print(id(a1))
print(id(a2))
print(id(a3))

结果:
__new__ is call
__init__ is call
__new__ is call
__init__ is call
__new__ is call
__init__ is call
1772512061104
1772512061104
1772512061104
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

结果可以看出实例对象地址相同

# 方式三:使用装饰器实现

此方式需要了解装饰器的一些知识,可参考 装饰器

def singleton(cls):
    _instance = {}

    def inner():
        if cls not in _instance:
            _instance[cls] = cls()
        return _instance[cls]
    return inner
    
@singleton
class Cls(object):
    def __init__(self):
        pass

cls1 = Cls()
cls2 = Cls()
print(id(cls1))
print(id(cls2))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

结果:

2323106588384
2323106588384
1
2

# 方式四:使用元类实现

此方式需要了解元类的一些知识,可参考 元类

class MyType(type):
    def __init__(self,name,base,attrs):
        self.instance = None
        super().__init__(name,base,attrs)

    def __call__(self, *args, **kwargs):
        # 判断是否有 instance
        if not self.instance:
            self.instance = self.__new__(self)
        self.__init__(self.instance,*args, **kwargs)
        return self.instance

class Foo(metaclass=MyType):
    def __init__(self):
        pass

v1 = Foo()
v2 = Foo()

print(v1)
print(v2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

结果:

<__main__.Foo object at 0x0000015BB8167DF0>
<__main__.Foo object at 0x0000015BB8167DF0>
1
2

(完)