单例模式(Singleton Pattern)

“单例模式(Singleton Pattern)是一个比较简单的模式,其定义如下:
Ensure a class has only one instance, and provide a global point of access to it.(确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。)”
Excerpt From: 秦小波 著. “设计模式之禅(第 2 版)(华章原创精品).” iBooks.

定义

UML 示例

可以通过plantuml查看

@startuml
class Singleton{
   + name
}
note top:Singleton has a 'name' property.此处的Singleton代表下边代码中的File类

class Client
note left: client get one singleton instance
Client --> Singleton
@enduml

由于此markdown不能展示故贴图
image.png

代码实现

import threading
import time


class Singleton(type):
    def __new__(cls, *args, **kwargs):
        return type.__new__(cls, *args, **kwargs)

    def __init__(self, *agrs, **kwargs):
        self.__instance = None
        super().__init__(*agrs, **kwargs)

    def __call__(self, *args, **kwargs):
        if self.__instance is None:
            # time.sleep(2) 模拟线程切换,此时不能保证多线程单例实现
            self.__instance = super().__call__(*args, **kwargs)
            return self.__instance
        else:
            return self.__instance


class File(metaclass=Singleton):
    def __init__(self, name):
        self.name = name


def create_file_instance(name):
    f = File(name)
    print(f)


# if __name__ == "__main__":
#     thread1 = threading.Thread(target=create_file_instance, args=('abc', ))
#     thread2 = threading.Thread(target=create_file_instance, args=('bcd', ))

#     thread1.start()
#     thread2.start()
# 如果有sleep会出现此情况:
#<__main__.File object at 0x1052d34a8>
#<__main__.File object at 0x1052d3390>
# 如果没有高概率不会出现不一致的情况,因为代码消耗的cpu时间很短,不足以造成线程切换
# -------------------多线程下单例实现---------------- #


def synchronized(func):
    func.__lock__ = threading.Lock()

    def lock_func(*args, **kwargs):
        with func.__lock__:
            return func(*args, **kwargs)

    return lock_func


class SingletonSync(type):
    def __init__(self, *agrs, **kwargs):
        self.__instance = None
        super().__init__(*agrs, **kwargs)

    @synchronized
    def __call__(self, *args, **kwargs):
        if self.__instance is None:
            time.sleep(2)
            self.__instance = super().__call__(*args, **kwargs)
            return self.__instance
        else:
            return self.__instance


class FileSync(metaclass=SingletonSync):
    def __init__(self, name):
        self.name = name


def create_file_instance_sync(name):
    f = FileSync(name)
    print(f)


if __name__ == "__main__":
    thread1 = threading.Thread(target=create_file_instance_sync, args=('abc', ))
    thread2 = threading.Thread(target=create_file_instance_sync, args=('bcd', ))

    thread1.start()
    thread2.start()
#<__main__.FileSync object at 0x10fbd43c8>
#<__main__.FileSync object at 0x10fbd43c8>
参考代码:Python Cookbook - 9.13.2 章节
同步锁机制:https://blog.csdn.net/lucky40...

知识点

学习《设计模式之禅》这本书,但又没有使用 java 语言,便突发奇想,结合设计模式的书,参考 python 的相关书籍编写 python 的设计模式的例子。相对于 java 来说单例应该是设计模式中最好理解和最好编写的代码了,但是对于 python 而言,小白的我,还是学习了很多知识点。

  • metaclass ( __metaclass__ )
    metaclass 可以指定某个类的元类,何为元类?就是创建类的类。 - 存在意义:拦截类的创建、修改类、返回修改之后的类。 - 回到我们的单例模式,就是利用元类来实现的,Singleton 类就是 File 类的元类,可以理解为是 Singleton 创建了 File 类,(注意是类而不是类的实例)。具体就是调用了__new__方法。

    • __metaclass__是实现自定义元类的一种方式,不多解释,有兴趣的可以参考如下两边文章,说的比较好,而且有 metaclass 更好的应用场景介绍

      https://www.cnblogs.com/Simon... https://www.liaoxuefeng.com/wiki/1016959663602400/1017592449371072
  • __new__ 方法:非常基础的知识点,newe 函数是执行 init 之前执行的函数,真正创建类的是 new 函数,new 函数会逐级查找__metaclass__(元类方法),如果找到就执行,找不到就继续查找父类,如果都没有最终会调到 type,type 也是一个元类,而且是像 int、string 等的元类,就是 type 创建了 int、string
  • __call__ 方法:File()执行的时候被调度的函数。所有定义了 call 方法的类都可以被称为是可执行的,就是可以当成一个函数执行。

说了这几个知识点,如果不太懂元类,或者 call 方法,可能还是不太好理解这个单例的执行,一个简单地方式就是回去跑一下试试。
说一下代码执行流程(只介绍 File 创建的流程):

在 File()执行之前,在 File 被定义传来的时候 Singleton 的 new 函数就被执行了,目的就是为了创建出这个 File 类,这也是为什么会写上 new 函数的原因,其实不写不会影响单例模式,然后 Singleton 的 init 被执行,目的是为了创建 instance 实例,实例名称也有讲究,双下划线表示隐私属性不会被复写。接着,当 File 函数被执行时会调用 call 方法。

sleep 函数是为了模拟线程间切换,如果不切换高概率是不会出现创建两个不同的实例的情况。

下集预告:工厂模式

说给自己听,当是给自己学习的一个动力吧!


neilliu
59 声望9 粉丝

coder is coding code snippet,coder change the world!