资讯专栏INFORMATION COLUMN

用Python实现设计模式——单例模式

CollinPeng / 799人阅读

摘要:前言单例模式是设计模式中最简单最容易理解的一种,维基百科的定义如下单例模式,也叫单子模式,是一种常用的软件设计模式。

前言

单例模式是设计模式(Design Pattern)中最简单、最容易理解的一种,维基百科[1]的定义如下:

单例模式,也叫单子模式,是一种常用的软件设计模式。在应用这个模式时,单例对象的类 "类 (计算机科学)")必须保证只有一个实例存在。许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。

单例模式的主要优点是共享资源和减少资源消耗,主要应用于IO或数据库的线程池,缓存,日志,对话和需共享数据的资源等,但是在实现情况中滥用单例模式会带来很多意想不到的问题,本文重点在于介绍几种Python实现单例模式的方法,这里就不再展开论述了。文中所演示的代码都会托管在Github上。

简单实现

首先,我们先尝试用Python内部类(嵌套类)来实现单例模式:

#coding=utf-8
class Singleton:
    """单列类
    """
    class __MyClass:
        """实际生成实例的类
        """
        def __init__(self, arg):
            """初始化并赋值"""
            self.foo = arg

        def display(self):
            """返回实例的id和属性值"""
            return (id(self), self.foo)

    # 类属性
    _instance = None
    def __init__(self, arg):
        if not Singleton._instance:
            Singleton._instance = Singleton.__MyClass(arg)
        else:
            Singleton._instance.foo = arg

    def __getattr__(self, attr):
        return getattr(self._instance, attr)

注意实际生成实例的类是内部的“__MyClass”类,前面的双下划线代表这是一个私有的类,用户不能再外面直接访问它。而在"__MyClass"类外封装了一个“Singleton”类,这个类的任务就是在初始化时保证整个上下文中只有一个实例,实现的方式很简单。用一个私有属性__instance_保存当前生成的实例,在初始化时判断实例是否为_None_,如果是就用“__MyClass”类生成一个新实例并赋值给__instance_,否就直接返回或调用当前__instance_的实例。最后用"__MyClass"里的实现的方法测试一下:

if __name__ == "__main__":
    """测试"""
    s1 = Singleton("bar")
    s2 = Singleton("zoo")
    print(s1.display())
    print(s2.display())

# output
>(41706760L, "zoo")
>(41706760L, "zoo")
基类

现在我们考虑将inner class拆分出来,因为在Python类实例化时会调用___new___方法[2]来生成实例,所以我们可以先继承“Singleton”类,然后通过重写基类的___new___方法让其实现单例模式:

#coding=utf-8
class Singleton(object):
    """单例类
    """
    _instance = None

    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
        return cls._instance

class MyClass(Singleton):
    """实际生成实例的类
    """
    def __init__(self, arg):
        self.foo = arg

    def display(self):
        return (id(self), self.foo)

测试结果:

if __name__ == "__main__":
    s1 = MyClass("bar")
    s2 = MyClass("zoo")
    print(s1.display())
    print(s2.display())
    assert s1 is s2

# output
>(40882416L, "zoo")
>(40882416L, "zoo")
装饰器

第三种就是最常见的用装饰器来实现单列模式:

#coding=utf-8
def singleton(cls):
    instances = {}
    def wrapper(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]
    return wrapper

@singleton
class MyClass:
    """实际生成实例的类
    """
    foo = "foo"
    def display(self):
        return (id(self))

@singleton
class OtherClass:
    """另一个类
    """
    pass

装饰器的实现过程是将生成的实例都放到一个名为_instances_的Dict中映射好,这样每次在类初始化时先检查_instances_中是否已经包含有例化好的实例,有就直接返回是咧,没有则调用类初始化一个并赋值给_instances_列表。装饰器的好处在于用一个Dict列表来管理所有需要实现单例模式的类,更简便和通用化。代码的测试结果如下:

if __name__ == "__main__":
    s1 = MyClass()
    s1.foo = "bar"
    print(s1.display(), s1.foo)
    s2 = MyClass()
    s2.foo = "zoo"
    print(s2.display(), s2.foo)
    assert s1 is s2
    s3 = OtherClass()
    s4 = OtherClass()
    assert s3 is s4
元类

如果希望不仅仅是通过限制而是在源头上就创建一个单例类,我们需要用到元类来实现,元类可以参考Stackoverflow[3]上的一个解答。简单的说就是Python中的类也是一种对象,被称为类对象。类对象可以通过元类type来创建,而在此过程中会调用type__call__ 方法。所以我们只要在type创建类对象的过程中重写 __call__ 方法,在其中加入相应的创建单例的逻辑即可实现单例模式,具体代码实现如下:

#coding=utf-8
class Singleton(type):
    def __call__(cls, *args, **kwargs):
        """重写,实现单例模式"""
        if not hasattr(cls, "_instance"):
            cls._instance = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instance

class MyClass(object):
    # 指定元类
    __metaclass__ = Singleton

    def display(self):
        return (id(self))

代码的测试与前面类似,这里就不再累述了。

线程安全

最后,需要注意的是单例模式在多线程下可能会出现线程安全的问题,这时候就需要在单例的初始化过程中加上线程同步锁来避免,但这样又会降低整体的性能,具体可以参考这篇文档。

参考

[1]维基百科
[2]Python官方文档
[3]Stackoverflow

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/41304.html

相关文章

  • Python单例模式(Singleton)的N种实现

    摘要:本篇文章总结了目前主流的实现单例模式的方法供读者参考。使用实现单例模式同样,我们在类的创建时进行干预,从而达到实现单例的目的。 很多初学者喜欢用 全局变量 ,因为这比函数的参数传来传去更容易让人理解。确实在很多场景下用全局变量很方便。不过如果代码规模增大,并且有多个文件的时候,全局变量就会变得比较混乱。你可能不知道在哪个文件中定义了相同类型甚至重名的全局变量,也不知道这个变量在程序的某...

    Maxiye 评论0 收藏0
  • Python new 类方法和 init 实例方法以及单例模式的简单讨论

    摘要:中的类都是单例模式一天,一同事问我这样一个问题。与方法属于新式类,即属于类。方法在实例被创建之后被调用,该方法仅仅是对方法创建的实例进行一些初始化操作。需要注意的是,在重写方法与方法的参数应该保持一致,否则会有发生。 Python 中的类都是单例模式? 一天,一同事问我这样一个问题。这是一个奇怪的问题,可能你也这么认为。这里先不做解释,我们先来看看 __new__ 和 __init__...

    FingerLiu 评论0 收藏0
  • Python 中的单例模式

    摘要:在中,我们可以用多种方法来实现单例模式使用模块使用使用装饰器使用元类使用模块其实,的模块就是天然的单例模式,因为模块在第一次导入时,会生成文件,当第二次导入时,就会直接加载文件,而不会再次执行模块代码。 单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。当你希望在整个系统中,某个类只能出现一个实例时,单例对...

    khlbat 评论0 收藏0
  • python设计模式-单例模式

    摘要:实现实现单例模式有多种方案使用提供了非常易用的类,只要继承它,就会成为单例。参考链接单例模式最后,感谢女朋友支持。 问题:现代化的巧克力工厂具备计算机控制的巧克力锅炉。锅炉做的事情就是把巧克力和牛奶融在一起,然后送到下一个阶段,以制成巧克力棒。下边是一个巧克力公司锅炉控制器的代码,仔细观察一下,这段代码有什么问题? class ChocolateBoiler(object): ...

    darkbug 评论0 收藏0
  • Python中的单例模式

    摘要:使用元类可以控制类的创建过程,它主要做三件事拦截类的创建修改类的定义返回修改后的类使用元类实现单例模式的代码如下执行结果 单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。 比如,某个服务器程序的配置信息存放在一个文件中,客户端通过一个 ...

    church 评论0 收藏0

发表评论

0条评论

CollinPeng

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<