单例模式(Python实现)

  单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中,应用该模式的类一个类只有一个实例。即一个类只有一个对象实例。

适用性
  单例模式的应用一般会有以下条件:

  1. 资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如日志文件,应用配置。
  2. 控制资源的情况下,方便资源之间的互相通信。如线程池等。

应用场景
  列举几个实际中的应用场景:

  1. 网站的计数器,一般也是采用单例模式实现,否则难以同步。
  2. 应用程序的日志应用,一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。
  3. Web应用的配置对象的读取,一般也应用单例模式,这个是由于配置文件是共享的资源。
      

起步

  在实现单例模式之前,先介绍使用到的知识点

1. __new__

  __init__方法通常用在初始化一个类实例的时候。但是__init__其实并不是实例化一个类的时候第一个被调用的方法,最先被调用的方法其实是__new__方法,__init__是在类实例创建之后调用,而__new__方法正是创建这个类实例的方法。

  • 继承自object的新式类才有__new__
  • __new__至少要有一个参数cls,代表要实例化的类,此参数在实例化时由Python解释器自动提供
  • __new__必须要有返回值,即返回实例化出来的实例,这点在自己实现__new__时要特别注意,可以return父类__new__出来的实例,或者直接是object的__new__出来的实例
  • __init__有一个参数self,就是这个__new__返回的实例,__init__在__new__的基础上可以完成一些其它初始化的动作,__init__不需要返回值
  • 若__new__没有正确返回当前类cls的实例,那__init__是不会被调用的,即使是父类的实例也不行

2. __dict__

  python中__dict__存储了该对象的一些属性。类和实例分别拥有自己的__dict__,且实例会共享类的__dict__。

3. 装饰器

参考我另一篇博文Python闭包和装饰器


实现

1. 重写__new__方法

通过重写__new__方法继承自该类的类只能创建一个实例

class Singleton(object):
def __new__(cls, *args, **kwargs):
    if not hasattr(cls, '_instance'):
        temp = super(Singleton, cls)
        cls._instance = temp.__new__(cls, *args, **kwargs)
    return cls._instance

class MyClass(Singleton):
    p = '单例类'

2. 使用装饰器

  实现装饰器之后,对类进行装饰即可实现一个单例类

def singleton(cls, *args, **kwargs)
    instances = {}
    def get_instance():
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
            return instance[cls]
        return get_instance

@singleton
class MyClass:
    p = '单例类'

3. 使用__dict__共享属性

  通过将所有实例的__dict__设置为同一个,即将实例的dict设为类变量。

class Singleton(object):
    _state = {}
    def __new__(cls, *args, **kwargs):
        _instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
        _instance.__dict__ = cls._state
        return _instance

class MyClass(Singleton):
    p = '单例类'

  使用这种方式实现单例模式,每次都会创建出新的实例,但是这些实例操作的是同样的数据。

结语

以下代码测试各方式实现的单例模式中创建的实例是否为同一个:

if __name__ == '__main__':
    e1 = MyClass()
    e2 = MyClass()
    if e1 is e2:
        print(1)
    else:
        print(0)

  经过测试,使用__new__方法和装饰器版本的是同一个实例,而使用__dict__则是不同的实例
  另外,据说通过使用import引入一个创建好的实例,是python中天然的单例模式

展开阅读全文

没有更多推荐了,返回首页