Jiang's blog

python单例模式的实现

Word count: 1.2kReading time: 5 min
2020/03/04 Share

单例模式

什么是单例模式

单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。

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

单例模式的实现

python实现单例模式有多种实现方法,下面一一来解释说明

1. 基于装饰器的单例模式的实现

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
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(object):
a = 1

def __init__(self, x=0):
self.x = x
print('这是A的类的初始化方法')

a1 = A(2)
a2 = A(3)
print(a1.x, a2.x)
print(id(a1), id(a2))

可以看到打印结果为:

3IYk1H.png

2. 基于类方法的单例模式的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class A:

def __init__(self, x, y):
self.x = x
self.y = y

@classmethod
def singleton(cls, *args, **kwargs):
if not hasattr(cls, '__instance'):
cls.__instance = cls(*args, **kwargs)
return cls.__instance


obj1 = A(1, 2)
obj2 = A(3, 4)
print(obj1.x, obj1.y, obj2.x, obj2.y)
print(id(obj1), id(obj2))
obj3 = A.singleton(1, 2)
obj4 = A.singleton(3, 4)
print(obj3.x, obj3.y, obj4.x, obj4.y)
print(id(obj3), id(obj4)

打印结果如下:

3IaYzF.png

但是这个实现方法在多线程上会出现堵塞现象,所以需要加锁

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
import threading


class A:
_instance_lock = threading.Lock()

def __init__(self, x, y):
self.x = x
self.y = y

@classmethod
def singleton(cls, *args, **kwargs):
if not hasattr(cls, '__instance'):
with cls._instance_lock:
cls.__instance = cls(*args, **kwargs)
return cls.__instance


obj1 = A(1, 2)
obj2 = A(3, 4)
print(obj1.x, obj1.y, obj2.x, obj2.y)
print(id(obj1), id(obj2))
obj3 = A.singleton(1, 2)
obj4 = A.singleton(3, 4)
print(obj3.x, obj3.y, obj4.x, obj4.y)
print(id(obj3), id(obj4))

可以发现,使用类方法创建单例模式时,必须要调用该方法,否则直接调用类得到的并不是单例

3. 基于new方法实现单例模式(推荐)

  • 一个对象的实例化过程是先执行类的new方法,如果我们没有写,默认会调用object的new方法,返回一个实例化对象,然后再调用init方法,对这个对象进行初始化,我们可以根据这个实现单例.

  • 在一个类的new方法中先判断是不是存在实例,如果存在实例,就直接返回,如果不存在实例就创建.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import threading

class A:
_instance_lock = threading.Lock()

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


obj1 = A()
obj2 = A()
print(obj1, obj2)
print(id(obj1), id(obj2))

45.基于metaclass方式实现

  1. 类由type创建,创建类时,type的init方法自动执行,类() 执行type的 call方法(类的new方法,类的init方法)

  2. 对象由类创建,创建对象时,类的init方法自动执行,对象()执行类的 call 方法

1
2
3
4
5
6
7
8
9
10
11
class Foo:
def __init__(self):
pass

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

obj = Foo()
# 执行type的 __call__ 方法,调用 Foo类(是type的对象)的 __new__方法,用于创建对象,然后调用 Foo类(是type的对象)的 __init__方法,用于对对象初始化。

obj() # 执行Foo的 __call__ 方法

元类的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class SingletonType(type):
#只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类
def __init__(self,*args,**kwargs):
super(SingletonType,self).__init__(*args,**kwargs)

def __call__(cls, *args, **kwargs): # 这里的cls,即Foo类
print('cls',cls)
# __new__()创建对象
obj = cls.__new__(cls,*args, **kwargs)
# __init__()实例化对象
cls.__init__(obj,*args, **kwargs) # Foo.__init__(obj)
return obj

class Foo(metaclass=SingletonType): # 指定创建Foo的type为SingletonType
def __init__(self,name):
self.name = name
def __new__(cls, *args, **kwargs):
return object.__new__(cls)

obj = Foo('xx')

实现单例模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

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(obj1,obj2)
CATALOG
  1. 1. 单例模式
    1. 1.1. 什么是单例模式
    2. 1.2. 单例模式的实现
      1. 1.2.1. 1. 基于装饰器的单例模式的实现
      2. 1.2.2. 2. 基于类方法的单例模式的实现
        1. 1.2.2.1. 但是这个实现方法在多线程上会出现堵塞现象,所以需要加锁
      3. 1.2.3. 3. 基于new方法实现单例模式(推荐)
      4. 1.2.4. 45.基于metaclass方式实现