zoukankan      html  css  js  c++  java
  • python设计模式之单例模式

    无论是在python代码中,还是面试中单例设计模式都是经常被问到和使用的,例如面试中会让你用代码实现单例模式分几种不同的方式,或者问你在平常工作中哪些地方有用到单例设计模式,然后深入探讨。

    在本文中我将针对这两个问题来回答和用python代码来编写我们的单例模式。

    首先,我们要了解什么是单例模式--官方解释是:确保一个类只有一个实例(也就是类的对象),并且提供一个全局的访问点(外部通过这个访问点来访问该类的唯一实例)。通俗的说就是,无论类实例化多少次,都只有一个相同的对象产生,并且可以通过一个具柄去访问这个这个唯一实例。

    其次,我们平时代码中碰到是用单例模式设计的代码有哪些呢?

    1. 应用框架的配置文件config(如flask,django框架的配置文件)
    2. 线程池,数据库连接池的创建
    3. 应用程序的日志应用

    这些应用都有一个共性那就是:他们都是全局共享的对象不会发生变化,不希望重复创建,这样有利于节省空间和减少性能消耗。

    python实现单例模式的几种方式分别有:

    1. 通过类方法实现
    2. 通过装饰器实现
    3. 通过__new__方法实现
    4. 通过元类实现

    一、类方法实现

    class SingleType:
        _instance = None
    
        @classmethod
        def instance(cls):
            if not cls._instance:
                cls._instance = SingleType()
            return cls._instance
    
    for i in range(100):
        print(SingleType.instance())

    看一下打印结果:

     返回的都是同一个对象,实现了我们说的,无论调用多少次,都只返回同一个实例,但是这在多线程的环境中,可能会存在返回多个实例,我们实验一下。

    import threading
    import time
    
    
    class SingleType:
        _instance = None
        def __init__(self):
            time.sleep(1)
            super().__init__()
    
        @classmethod
        def instance(cls):
            if not cls._instance:
                cls._instance = SingleType()
            return cls._instance
    
    def task():
        obj = SingleType.instance()
        print(obj)
    
    for i in range(10):
        t = threading.Thread(target=task).start()
        print(SingleType.instance())
    

     这是多线程环境测试的代码结果如下:

     可以看到在多线程的环境中,我们产生了两个2个对象,这就违背了我们的单例原则了,那怎么解决它呢,答案是加锁。

    import threading
    import time
    
    
    class SingleType:
        _instance = None
        _lock = threading.Lock()
    
        def __init__(self):
            time.sleep(1)
            super().__init__()
    
        @classmethod
        def instance(cls):
            if not cls._instance:
                with SingleType._lock:
                    if not cls._instance:
                        cls._instance = SingleType()
            return cls._instance
    
    def task():
        obj = SingleType.instance()
        print(obj)
    
    for i in range(10):
        t = threading.Thread(target=task).start()

    加锁后我们看运行结果:

     这样就成功解决在多线程环境下单例的问题了, 至于问什么在最外层还要加一个if判断,是为了提高程序的性能。

    二、装饰器模式实现

    这种实现方式在我上一片将装饰器一文中是有提及的,在这我们再实现一次

    def decorator(cls):
        def wrap(*args, **kwargs):
            if not cls._instance:
                cls._instance = cls()
            return cls._instance
        return wrap
    
    
    @decorator
    class SingleType:
        _instance = None
    
        def __init__(self):
            super().__init__()
    
    for i in range(10):
        obj = SingleType()
        print(obj)

    当然这里也会出现多线程创建多个实例的问题,可以参照上一个例子自行去实现多线程的版本。

    三、使用__new__方法实现

    在说__new__方法实现之前,我们需要有个前提知识,我们对象在实例话时会调用__init__方法,但是在调用__init__方法之前,类会先调用__new__方法,__new__方法中决定实例化怎样的对象(可以是调用object的__new__方法正常接着实例话,可以生成其他类的实例等)具体知识可以自行去补充,本文主要讲单例模式。

    class SingleType:
        _instance = None
    
        def __new__(cls, *args, **kwargs):
            if not cls._instance:
                cls._instance = super().__new__(cls)
            return cls._instance
    
        def __init__(self):
            super().__init__()
    
    
    for i in range(10):
        obj = SingleType()
        print(obj)

    查看运行结果:

    四、使用元类实现单例模式

    前提知识: 实例化对象时方法调用顺序是: type类的__cal__方法,其中会调用__new__方法和__init__方法

    import threading
    
    
    class SingleType(type):
        lock = threading.Lock()
    
        def __call__(cls, *args, **kwargs):
            if not cls._instance:
                with SingleType.lock:
                    if not cls._instance:
                        cls._instance = super().__call__(*args, **kwargs)        # 这个地方也可以替换成 obj = cls.__new__(cls) --> cls.__init__(onj) --> cls._instance=obj
            return cls._instance
    
    
    class Test(metaclass=SingleType):
        _instance = None
    
    
    for i in range(10):
        obj = Test()
        print(obj)

    在代码注释部分的作用相当于自己实现type.__call__的功能。

    至此python实现单例设计模式所有的实现方式已经讲完,既然说的是设计模式就要把设计模式的八大准则贴出来,时常警醒自己。

    1、依赖倒置原则(DIP):高层模块不依赖于低层模块,而是都依赖于抽象;抽象不依赖于实现细节,实现细节应该依赖于抽象。
     
    2、开放封闭原则(OCP):对扩展开放,对依赖关闭;类的模块是可扩展的,但是不可修改。
     
    3、单一职责原则(SRP):一个类仅有一个能引起他的变化,变化的方向隐含着类的责任。
     
    4、LisKov替换原则:派这类能够替换他的基类,继承表达了类的抽象。
     
    5、接口隔离原则(ISP):不应该强迫客户程序依赖他们不用的方法,接口应该小而强大。
     
    6、优先使用对象组合,而不是类继承:类的继承破坏了封装性,子类父类耦合度高,类继承通常称为“白箱复用和“,对象组合称为”黑箱复用”。并且对象组合只需要有良好的定义的接口,且耦合度低。
     
    7、封装变化点:使用封装来创建对象之间的分界层,让设计者可以在分界层的一侧修改,而不会对另一侧产生不良影响。
     
    8、针对接口编程,而不是针对实现编程:减少系统中个部分的依赖关系,从而实现”高内聚,松耦合“的类型设计方案;不将变量类型声明为某个特定的具体类,而是声明为某个接口。 
  • 相关阅读:
    django 项目需要注意的一些点
    VUE之路
    Oracle 表格碎片的查看方法
    RHEL 6.x or 7.x 使用分区绑定ASM 磁盘的方法
    RMAN 修复主库 nologging 操作导致物理备库的坏块
    Oracle 数据库19c 回退降级到 11.2.0.4 方案
    如何评估oracle 数据库rman全备和增量备份大小
    在将Oracle GI和DB升级到19c或降级到以前的版本之前需要应用的补丁 (Doc ID 2668071.1)
    Oracle 数据库坏块处理
    opatch auto 安装11.2.0.4.20190115 PSU遇到 OUI-67133: Execution of PRE script failed,with returen value 1 报错
  • 原文地址:https://www.cnblogs.com/lifei01/p/13187852.html
Copyright © 2011-2022 走看看