前言

本篇博客归类于设计模式,单例模式算是我了解的不多的设计模式之一,在某些应用场景下为了节省资源,常常使用单例模式来编写代码。

单例模式(Singleton Pattert)是一种常用的软件设计模式,该模式的主要目的是确保在某一个类中能且只能产生一个实例。当在某些场景下希望某个类只能产生一个实例时,就可以使用单例模式了。

python 中的单例模式

单例模式也叫单子模式,是一种常用的软件设计模式。在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个全局对象,这样有利于我们协调系统整体的行为。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。

实现单例模式的思路是:一个类能返回对象一个引用(永远是同一个)和一个获得该实例的方法(必须是静态方法,通常使用getInstance这个名称);当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用;同时我们还将该类的构造函数定义为私有方法,这样其他处的代码就无法通过调用该类的构造函数来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例。

单例模式在多线程的应用场合下必须小心使用。如果当唯一实例尚未创建时,有两个线程同时调用创建实例方法,那么它们同时没有检测到唯一实例的存在,从而同时各自创建了一个实例,这样就有两个实例被构造出来,从而违反了单例模式中实例惟一的原则。解决这个问题的办法是为指示是否已经实例化的变量提供一个互斥锁(虽然这样会降低效率)。

在面向对象编程中,学习了使用某一个类来创建多个不同的对象,那么如何使得该类只能产生一个对象呢?

在Python 中,可以使用多种方法来实现单例模式:

  1. 使用模块
  2. 使用魔术方法__new__
  3. 使用装饰器
  4. 使用元类

使用模块

python 中的模块就是天然的单例模式,因为模块只会在第一次导入的时候加载,会生成一个 .pyc文件,第二次导入时,就会直接加载 .pyc文件,而不会再次执行模块代码。因此,可以把相关的代码和数据定义在一个模块中,就可以获得一个单例对象了。然后要用的话导入就可以,我可以保证这样做的话肯定只有一个单例对象。代码就不演示了。

使用特殊方法__new__

在使用类实例化一个对象时,其实python 解释器做了两步操作:

  1. 调用 object 的__new__方法创建一个空对象;
  2. 调用类本身的__init__方法进行初始化(这都是通过元类的__call__来间接调用的)

那么是不是可以在调用__new__的时候做一些判断来使得只能创建一个单例对象呢?

class Singleinstance:
    _instance = None
    def __new__(cls, *arg, **kwargs):
        if not cls._instance:
            cls._instance = super(Singleinstance, cls).__new__(cls, *args, **kwargs)
        return cls._instance
class MyClass(Singleinstance):
    a = 1

在上面的代码中,将类的__new__方法和一个类的属性关联起来,如果 cls._instance 为 None 则创建一个对象,否则的话直接返回cls._instance。

使用装饰器

装饰器可以动态的修改一个类或者函数的功能。所以可以使用装饰器来装饰某各类,使其只能生成一个实例:

from functools import wraps

def singleinstance(cls):
    instances = {}
    @wraps(cls)
    def getinstance(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]
    return getinstance

@singleinstance
class MyClass:
    a = 1

在上面,定义了一个装饰器 singleinstance,它返回了一个内部函数getinstance,该函数会判断某个类是否在字典 instance 中,如果不存在,则会将 cls 作为 key,cls(*args, **kwargs)作为 value存到 instances 中,否则,直接返回 instances[cls]。该装饰器可以装饰所有只需要产生一个实例的类上。

使用 metaclass

元类 metaclass 可以通过自定义__new__方法和__init__方法来控制类的创建过程,它主要做三件事:

  • 拦截类的创建
  • 修改类的定义
  • 返回修改后的类

使用元类实现单例的代码如下:

class MyMetaClass(type):

    # def __init__(self):
    #     pass

    instance = None
    def __call__(cls, *args, **kwargs):
        # 判断对象有没有创建过
        if not MyMetaClass.instance:
            # 创建空对象
            MyMetaClass.instance = object.__new__(cls)
            print(cls.__name__)
            print(cls.__dict__)
            print("创建新的播放器对象!")
            # 初始化对象
            MyMetaClass.instance.__init__(*args,**kwargs)
            print(type(MyMetaClass.instance))
            # 返回对象
        return MyMetaClass.instance

class CDPlayer(metaclass=MyMetaClass):
    pass     
11-30 14:29