python单例模式的实现

单例模式看wikipedia的解释:

单例模式,也叫单子模式,是一种常用的软件设计模式。在应用这个模式时,单例对象的类必须保证只有一个实例存在。

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

实现单例模式的思路是:

  • 一个类能返回对象一个引用(永远是同一个)和一个获得该实例的方法(必须是静态方法,通常使用getInstance这个名称)。

  • 当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用。

  • 同时我们还将该类的构造函数定义为私有方法,这样其他处的代码就无法通过调用该类的构造函数来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例。

首先,由于python不强制隐私,访问权限不像Java那样严格执行,所以它没有私有构造函数这一说,全凭你自觉不要访问敏感信息。其次,在python中最接近的构造函数是__new__(),但这很少使用。通常使用__init__(self),由它修改刚刚创建的对象(self代表实例本身)。最后,python既支持函数式编程又支持面向对象编程。

下面介绍两种多线程安全的单例模式python实现。

1. 懒汉式(双重校验锁)

# test.py
import threading
import time


class LazySingleton(object):
    # 类变量,__的写法代表这是一个私有变量
    # 创建一个线程锁
    __lock = threading.Lock()

    def __init__(self):
        print('初始化了一个类对象')
        # 实现阻塞5s中
        time.sleep(5)

    # 声明一个类方法,相当与java中静态(类)方法
    @classmethod
    def get_instance(cls, *args, **kwargs):
        try:
            # 访问类变量__instance,如果不存在会按报错处理,存在就返回
            LazySingleton.__instance
            return LazySingleton.__instance
        except:
            # 在执行with块代码的时候加上线程锁,执行完毕释放线程锁
            # 此线程锁不是GIL锁
            with LazySingleton.__lock:
                try:
                    # 访问类变量__instance,如果不存在会按报错处理,存在就返回
                    LazySingleton.__instance
                    return LazySingleton.__instance
                except:
                    # 给LazySingleton类绑定一个变量__instance,相当与Java中的静态(类变变量)
                    # __的写法代表这是一个私有变量,虽然是私有变量,但是你也可以在类外部访问,原则上你不要访问
                    LazySingleton.__instance = LazySingleton()
                    return LazySingleton.__instance


if __name__ == "__main__":
    def task():
        # 获取LazySingleton类实例/对象
        obj = LazySingleton.get_instance()
        print(obj)


    # 循环创建多个线程
    for i in range(10):
        t = threading.Thread(target=task)
        t.start()

提交执行:

ssh://appl@172.30.1.243:22/opt/appl/anaconda3/bin/python -u /opt/appl/pycharm-projects/spark_streaming_test/test.py
初始化了一个类对象
<__main__.LazySingleton object at 0x7f13104469e8>
<__main__.LazySingleton object at 0x7f13104469e8>
<__main__.LazySingleton object at 0x7f13104469e8>
<__main__.LazySingleton object at 0x7f13104469e8>
<__main__.LazySingleton object at 0x7f13104469e8>
<__main__.LazySingleton object at 0x7f13104469e8>
<__main__.LazySingleton object at 0x7f13104469e8>
<__main__.LazySingleton object at 0x7f13104469e8>
<__main__.LazySingleton object at 0x7f13104469e8>
<__main__.LazySingleton object at 0x7f13104469e8>

Process finished with exit code 0

2. 装饰器
如果不懂python的函数式编程建议把廖雪峰老师python 函数式编程 这一章看完,不然下面的代码理解会有困难。

python用装饰器实现单例模式思路有些改变了,要单例的类不用做任何改变,不用写获得该实例的方法,和正常类无区别。

# test2.py
import threading
from functools import wraps
import time


class Decorator(object):
    # 创建一个线程锁,这是一个类变量,类似于Java中静态变量
    __lock = threading.Lock()
    __instance = None

    # 定义一个高阶函数,cls参数是一个类
    # 同时它是一个静态方法,类似于Java中静态(类)方法
    @staticmethod
    def singleton(cls):
        # 定义一个私有方法,wraps作用不知道的自己查,不感兴趣的也不用知道
        @wraps(cls)
        def __wrapper(*args, **kw):
            # 如果类变量__instance不存在就新建,存在就返回
            if Decorator.__instance is None:
                # 在执行with块代码的时候加上线程锁,执行完毕释放线程锁
                # 此线程锁不是GIL锁
                with Decorator.__lock:
                    # 如果类变量__instance不存在就新建,存在就返回
                    if Decorator.__instance is None:
                        # 新建一个类
                        Decorator.__instance = cls(*args, **kw)
                    return Decorator.__instance
            return Decorator.__instance

        # 返回值为函数叫做闭包
        return __wrapper


# singleton函数是一个装饰器,它接受一个类或函数作为变量,并返回一个函数
# 创建类:DecoratorSingleton()相当于执行了singleton(DecoratorSingleton())
# lock是一个线程锁
@Decorator.singleton
class DecoratorSingleton(object):
    # 初始化方法
    def __init__(self, name):
        print('my name is ' + name)
        # 实现阻塞5s
        time.sleep(5)


if __name__ == "__main__":
    # 创建一个任务函数
    def task():
        # 获取DecoratorSingleton类实例/对象
        obj = DecoratorSingleton('dong')
        print(obj)


    # 循环创建多个线程
    for i in range(10):
        t = threading.Thread(target=task)
        t.start()

提交执行:

ssh://appl@172.30.1.243:22/opt/appl/anaconda3/bin/python -u /opt/appl/pycharm-projects/spark_streaming_test/test2.py
my name is dong
<__main__.DecoratorSingleton object at 0x7f4dcd5329e8>
<__main__.DecoratorSingleton object at 0x7f4dcd5329e8>
<__main__.DecoratorSingleton object at 0x7f4dcd5329e8>
<__main__.DecoratorSingleton object at 0x7f4dcd5329e8>
<__main__.DecoratorSingleton object at 0x7f4dcd5329e8>
<__main__.DecoratorSingleton object at 0x7f4dcd5329e8>
<__main__.DecoratorSingleton object at 0x7f4dcd5329e8>
<__main__.DecoratorSingleton object at 0x7f4dcd5329e8>
<__main__.DecoratorSingleton object at 0x7f4dcd5329e8>
<__main__.DecoratorSingleton object at 0x7f4dcd5329e8>

Process finished with exit code 0

利用装饰器实现的单例模式极其灵活,不仅可以用在类上面,还可以用在方法上面,对方法产生的结果进行单例子化。

Python还可以利用重写__new__()实现单例,有兴趣的同学可以自行研究。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 单例模式(SingletonPattern)一般被认为是最简单、最易理解的设计模式,也因为它的简洁易懂,是项目中最...
    成热了阅读 4,293评论 4 34
  • 这是16年5月份编辑的一份比较杂乱适合自己观看的学习记录文档,今天18年5月份再次想写文章,发现简书还为我保存起的...
    Jenaral阅读 2,858评论 2 9
  • Python 面向对象Python从设计之初就已经是一门面向对象的语言,正因为如此,在Python中创建一个类和对...
    顺毛阅读 4,238评论 4 16
  • 复习开国大典时间地点参加人物。再由学校每周升旗仪式引出集体活动流程,通过课件回顾升旗仪式流程各项,引出开国大典流程...
    这样生活阅读 992评论 0 0
  • 此文献给那些想要健身的人,无论你是增肌、减脂或者是增强体质 本人身高174CM,健身前体重60KG,三十出...
    hikerxu阅读 2,675评论 14 18