1.单例模式介绍

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

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

2.Python实现单例模式

  • 使用模块

    python的模块就是天然的单例模式,因为模块在第一次导入时, 会生成.pyc文件,当第二次导入时,就会直接加载.pyc文件,而不会再次执行模块代码, 因此,我们需要把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了

    1
    class Singleton(object):
    2
    3
        def foo(self):
    4
            pass
    5
    6
    7
    singleon = Singleton()

    将上述代码保存在单独文件中(use_model.py),使用时,直接在其他文件中导入此文件中的对象, 这个给对象就是单例模式的对象

  • 使用装饰器

    1
    def Singleton(cls):
    2
        _instance = {}
    3
    4
        def _singleton(*args, **kwargs):
    5
            if cls not in _instance:
    6
                _instance[cls] = cls(*args, **kwargs)
    7
            return
    8
    9
        return _singleton
    10
    11
    @Singleton
    12
    class A(object):
    13
        a = 1
    14
    15
        def __init__(self, x=0):
    16
            self.x = x
    17
    18
    19
    a1 = A(1)
    20
    a2 = A(2)
  • 使用类

    1
    class Singleton(object):
    2
    3
        def __init__(self):
    4
            pass
    5
    6
        @classmethod
    7
        def instance(cls, *args, **kwargs):
    8
            if not hasattr(Singleton, "_instance"):
    9
                Singleton._instance = Singleton(*args, **kwargs)
    10
    11
            return Singleton._instance

    注意:

    这种情况下不支持多线程

    举例:

    1
    import time
    2
    import threading
    3
    4
    5
    class Singleton(object):
    6
    7
        def __init__(self):
    8
           time.sleep(1) 
    9
    10
        @classmethod
    11
        def instance(cls, *args, **kwargs):
    12
            if not hasattr(Singleton, "_instance"):
    13
                Singleton._instance = Singleton(*args, **kwargs)
    14
    15
            return Singleton._instance
    16
    17
    18
    def task(arg):
    19
        obj = Singleton.instance()
    20
        print(obj)
    21
    22
    for i in range(10):
    23
        t = threading.Thread(target=task, args=[i,])
    24
        t.start()

结果如下:

1
<__main__.Singleton object at 0x7f43962c0550>
2
<__main__.Singleton object at 0x7f4396238390>
3
<__main__.Singleton object at 0x7f4396238470>
4
<__main__.Singleton object at 0x7f4396238550>
5
<__main__.Singleton object at 0x7f4396238630>
6
<__main__.Singleton object at 0x7f4396238710>
7
<__main__.Singleton object at 0x7f43962387f0>
8
<__main__.Singleton object at 0x7f43962388d0>
9
<__main__.Singleton object at 0x7f43962389b0>
10
<__main__.Singleton object at 0x7f4396238a90>

解决:

1
import time
2
import threading
3
4
5
class Singleton(object):
6
    _instance_lock = threading.Lock()
7
8
    def __init__(self):
9
       time.sleep(1) 
10
11
    @classmethod
12
    def instance(cls, *args, **kwargs):
13
        if not hasattr(Singleton, "_instance"):
14
            with Singleton._instance_lock:
15
                if not hasattr(Singleton, "_instance"):
16
                    Singleton._instance = Singleton(*args, **kwargs)
17
18
        return Singleton._instance
19
20
21
def task(arg):
22
    obj = Singleton.instance()
23
    print(obj)
24
25
for i in range(10):
26
    t = threading.Thread(target=task, args=[i,])
27
    t.start()

这种方式实现的单例模式,使用时会有限制,实例化时必须通过obj = Singleton.instance(), 如果使用obj = Singleton() 得到的不是单例

  • 基于__new__方法实现

    当我们实例化一个对象时,其实是先执行了类的__new___方法(默认调用object.__new__), 实例化对象,然后再执行类的__init__方法,对这个对象进行初始化, 基于这点,可以实现单例模式

    1
    import threading
    2
    import time
    3
    4
    5
    class Singleton(object):
    6
    7
        _instance_lock = threading.Lock()
    8
    9
        def __init__(self):
    10
            time.sleep(1)
    11
    12
        def __new__(cls, *args, **kwargs):
    13
            if not hasattr(Singleton, "_intance"):
    14
                with Singleton._instance_lock:
    15
                    if not hasattr(Singleton, "_instance"):
    16
                        Singleton._instance = object.__new__(cls)
    17
    18
            return Singleton._instance
    19
    20
    21
    obj_1 = Singleton()
    22
    obj_2 = Singleton()
    23
    print(obj_1, obj_2)
    24
    25
    def task(arg):
    26
        obj = Singleton()
    27
        print(obj)
    28
    29
    for i in range(10):
    30
        t = threading.Thread(target=task, args=[i,])
    31
        t.start()

    输出

    1
    <__main__.Singleton object at 0x7f4a2d9a5898> <__main__.Singleton object at 0x7f4a2d9a5898>
    2
    <__main__.Singleton object at 0x7f4a2d9a5898>
    3
    <__main__.Singleton object at 0x7f4a2d9a5898>
    4
    <__main__.Singleton object at 0x7f4a2d9a5898>
    5
    <__main__.Singleton object at 0x7f4a2d9a5898>
    6
    <__main__.Singleton object at 0x7f4a2d9a5898>
    7
    <__main__.Singleton object at 0x7f4a2d9a5898>
    8
    <__main__.Singleton object at 0x7f4a2d9a5898>
    9
    <__main__.Singleton object at 0x7f4a2d9a5898>
    10
    <__main__.Singleton object at 0x7f4a2d9a5898>
    11
    <__main__.Singleton object at 0x7f4a2d9a5898>
  • 基于metaclass实现

    类是由type实现的。创建类时, type的__init__方法自动执行,类()执行type的__call__方法

    对象由类创建,创建对象时,类的__init__方法自动执行,对象()执行类的__call__方法

    代码实现

    1
    import threading
    2
    3
    class SingletonType(type):
    4
    5
        _intance_lock = threading.Lock()
    6
    7
        def __call__(cls, *args, **kwargs):
    8
            if not hasattr(cls, "_instance"):
    9
                with SingletonType._intance_lock:
    10
                    if not hasattr(cls, "_instance"):
    11
                        cls._instance = super(SingletonType, cls).__call__(*args,
    12
                                                                           **kwargs)
    13
            return cls._instance
    14
    15
    16
    class Test(metaclass=SingletonType):
    17
    18
        def __init__(self, name):
    19
            self.name = name
    20
    21
    22
    if __name__ == '__main__':
    23
        obj_1 = Test('yhkl')
    24
        obj_2 = Test('name')
    25
        print(obj_1, obj_2)