python 单例模式

本文主要包含以下内容:

  1. 使用场景
  2. 一个较好的实现方式

使用场景

单例模式的主要目的就是确保某一个类只有一个实例存在。当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。如:

  1. 当每个实例都会占用资源,并且不需要维护每个实例的状态,而且实例初始化会影响性能:某个服务器程序的配置信息存放在一个文件中,客户端通过一个 AppConfig 的类来读取配置文件的信息。如果在程序运行期间,有很多地方都需要使用配置文件的内容,也就是说,很多地方都需要创建 AppConfig 对象的实例,这就导致系统中存在多个 AppConfig 的实例对象,而这样会严重浪费内存资源,尤其是在配置文件内容很多的情况下。事实上,类似 AppConfig 这样的类,我们希望在程序运行期间只存在一个实例对象。
  2. 当有同步需要的时候,可以通过一个实例来进行同步控制:Python的logger,网站计数器

一个较好的实现方式

这里就不采用装饰器的模式了,而是采用python的特性__new__,更加通俗易懂。

__new__:是在新式类中新出现的方法,它作用在构造方法建造实例之前,可以这么理解,在 Python 中存在于类里面的构造方法 __init__() 负责将类的实例化,而在 __init__() 启动之前,__new__() 决定是否要使用该 __init__() 方法,因为__new__() 可以调用其他类的构造方法或者直接返回别的对象来作为本类的实例。

上述总结下来就是__new__()会在__init__()实例化之前调用,所以可以在实例化之前判断该实例是否存在。

singleton.py

import threading


# 由于__new__是新式类出现的方法,所以使用python2时需要采用新式类的创建方式,python3随意
# python2:  class Singleton: ×
#           class Singleton(object): √
class Singleton(object):
    # 使用 threading.Lock() 来锁住线程,避免多个线程同时操作而创建多个实例
    _instance_lock = threading.Lock()

    def __init__(self):
        pass

    def __new__(cls, *args, **kwargs):
        # 双重检查,提升效率
        if not hasattr(Singleton, '_instance'):
            # with ... ->  _instance_lock.acquire()  _instance_lock.release()
            with Singleton._instance_lock:
                if not hasattr(Singleton, '_instance'):
                    Singleton._instance = object.__new__(cls)
        return Singleton._instance
    
    
if __name__ == '__main__':
    def task():
        obj = Singleton()
        print(obj)

    for i in range(10):
        t = threading.Thread(target=task)
        t.start()

    time.sleep(5)
    obj = Singleton()
    print(obj)

参考:
Python中的单例模式的几种实现方式的及优化
设计模式(Python)-单例模式
Python 之 new() 方法与实例化