资讯专栏INFORMATION COLUMN

python设计模式-单例模式

darkbug / 687人阅读

摘要:实现实现单例模式有多种方案使用提供了非常易用的类,只要继承它,就会成为单例。参考链接单例模式最后,感谢女朋友支持。

问题:现代化的巧克力工厂具备计算机控制的巧克力锅炉。锅炉做的事情就是把巧克力和牛奶融在一起,然后送到下一个阶段,以制成巧克力棒。下边是一个巧克力公司锅炉控制器的代码,仔细观察一下,这段代码有什么问题?

class ChocolateBoiler(object):

    def __init__(self):
        self.empty = True
        self.boiled = False

    def fill(self):
        # 向锅炉填充巧克力和牛奶混合物
        # 在锅炉内填充原料时,锅炉必须是空的。
        # 一旦填入原料,就要把empty 和 boiled 标志设置好
        if self.empty:
            self.empty = False
            self.boiled = False

    def drain(self):
        # 排出煮沸的巧克力和牛奶
        # 锅炉排出时,必须是满的且煮沸的。
        # 排出完毕empty 设置为 true
        if not self.empty and self.boiled:
            self.empty = True

    def boil(self):
        # 将颅内物煮沸
        # 煮混合物时,锅炉内必须是满的且没有煮沸过
        # 一旦煮沸,就把 boiled 设置为 true
        if not self.empty and not self.boiled:
            self.boiled = True

从代码可以看出,他们加入了多种判断,以防止不好的事情发生。如果同时存在两个ChocolateBoiler实例,那这么多判断岂不是失去作用了。那我们改如何实现这个需求呢?这个问题的核心是,我们要先判断实例是不是已经存在,如果存在就不再创建。

_chocolate_boiler_instance = None  # 声明实例

def chocolate_boiler():
    global _chocolate_boiler_instance  # 使用全局变量

    if _chocolate_boiler_instance is not None: # 判断是否存在,如果存在,直接返回
        return _chocolate_boiler_instance
    else:
        # 如果不存在,创建一个新的
        _chocolate_boiler_instance = ChocolateBoiler()
        return _chocolate_boiler_instance

现在我们需要获取 ChocolateBoiler 实例的时候只需要调用 chocolate_boiler 方法获取实例即可保证同时只有一个 ChocolateBoiler实例。

这种保证 ChocolateBoiler类只有一个实例,并提供一个全局访问点的模式,就是单例模式

单例模式 定义

单例模式:确保一个类只有一个实例,并提供一个全局访问点。

也就是说,我们使用单例模式要把某个类设计成自己管理的一个多带带实例,同时也避免其他类再自行产生实例。并且只允许通过单例类获取单例的实例。

我们也提供对这个实例的全局访问点:当你需要实例时,像类查询,它会返回单个实例。

实现

python 实现单例模式有多种方案:

使用 metaclass

《python cookbook》提供了非常易用的 Singleton 类,只要继承它,就会成为单例。

# python 3 代码实现
class Singleton(type):

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

    def __call__(self, *args, **kwargs):
        if self.__instance is None:
            # 如果 __instance 不存在,创建新的实例
            self.__instance = super().__call__(*args, **kwargs)
            return self.__instance
        else:
            # 如果存在,直接返回
            return self.__instance


class Spam(metaclass=Singleton):

    def __init__(self):
        print("Creating Spam")

a = Spam()
b = Spam()

print(a is b)  # 这里输出为 True

元类(metaclass)可以控制类的创建过程,它主要做三件事:

拦截类的创建

修改类的定义

返回修改后的类

例子中我们构造了一个Singleton元类,并使用__call__方法使其能够模拟函数的行为。构造类 Spam 时,将其元类设为Singleton,那么创建类对象 Spam 时,行为发生如下:

Spam = Singleton(name,bases,class_dict),Spam 其实为Singleton类的一个实例。

创建 Spam 的实例时,Spam()=Singleton(name,bases,class_dict)()=Singleton(name,bases,class_dict).__call__(),这样就将 Spam 的所有实例都指向了 Spam 的属性 __instance上。

使用 new

我们可以使用 new 来控制实例的创建过程,代码如下:

class Singleton(object):

    __instance = None

    def __new__(cls, *args, **kw):
        if not cls.__instance:
            cls.__instance = super().__new__(cls, *args, **kw)
        return cls.__instance

class Foo(Singleton):
    a = 1

one = Foo()
two = Foo()
assert one == two
assert one is two
assert id(one) == id(two)

通过 new 方法,将类的实例在创建的时候绑定到类属性 __instance 上。如果cls.__instance 为None,说明类还未实例化,实例化并将实例绑定到cls.__instance 以后每次实例化的时候都返回第一次实例化创建的实例。注意从Singleton派生子类的时候,不要重载__new__。

使用装饰器
import functools

def singleton(cls):
    """ Use class as singleton. """
    # 首先将 __new__ 方法赋值给 __new_original__
    cls.__new_original__ = cls.__new__

    @functools.wraps(cls.__new__)
    def singleton_new(cls, *args, **kw):
        # 尝试从 __dict__ 取 __it__
        it =  cls.__dict__.get("__it__")
        if it is not None: # 如果有值,说明实例已经创建,返回实例
            return it
        # 如果实例不存在,使用 __new_original__ 创建实例,并将实例赋值给 __it__
        cls.__it__ = it = cls.__new_original__(cls, *args, **kw)
        it.__init_original__(*args, **kw)
        return it
    # class 将原有__new__ 方法用 singleton_new 替换
    cls.__new__ = singleton_new
    cls.__init_original__ = cls.__init__
    cls.__init__ = object.__init__

    return cls

#
# 使用示例
#
@singleton
class Foo:
    def __new__(cls):
        cls.x = 10
        return object.__new__(cls)

    def __init__(self):
        assert self.x == 10
        self.x = 15


assert Foo().x == 15
Foo().x = 20
assert Foo().x == 20

这种方法的内部实现和使用 __new__ 类似:

首先,将 new 方法赋值给 new_original__,原有 new 方法用 singleton_new 替换,定义 init_original 并将 cls.__init 赋值给 init_original

在 singleton_new 方法内部,尝试从 dict 取 __it__(实例)

如果实例不存在,使用 new_original 创建实例,并将实例赋值给 __it__,然后返回实例

最简单的方式

将名字singleton绑定到实例上,singleton就是它自己类的唯一对象了。

class singleton(object):
    pass
singleton = singleton()

https://github.com/gusibi/Metis/blob/master/apis/v1/schemas.py#L107 使用的就是这种方式,用来获取全局的 request

Python 的模块就是天然的单例模式,因为模块在第一次导入时,会生成 .pyc 文件,当第二次导入时,就会直接加载 .pyc 文件,而不会再次执行模块代码。因此,我们只需把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。

参考链接

Creating a singleton in Python

Python单例模式

Why is __init__() always called after __new__()?


最后,感谢女朋友支持。

欢迎关注(April_Louisa) 请我喝芬达

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

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

相关文章

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

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

    Maxiye 评论0 收藏0
  • Python实现设计模式——单例模式

    摘要:前言单例模式是设计模式中最简单最容易理解的一种,维基百科的定义如下单例模式,也叫单子模式,是一种常用的软件设计模式。 前言 单例模式是设计模式(Design Pattern)中最简单、最容易理解的一种,维基百科[1]的定义如下: 单例模式,也叫单子模式,是一种常用的软件设计模式。在应用这个模式时,单例对象的类 类 (计算机科学))必须保证只有一个实例存在。许多时候整个系统只需要拥有一...

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

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

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

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

    church 评论0 收藏0
  • Python设计模式

    摘要:简单工厂模式工厂模式有一种非常形象的描述,建立对象的类就如一个工厂,而需要被建立的对象就是一个个产品在工厂中加工产品,使用产品的人,不用在乎产品是如何生产出来的。单例模式的单例模式,所谓单例模式就是一个类只能创建一个实例化。 简单工厂模式 工厂模式有一种非常形象的描述,建立对象的类就如一个工厂,而需要被建立的对象就是一个个产品;在工厂中加工产品,使用产品的人,不用在乎产品是如何生产出来...

    khs1994 评论0 收藏0

发表评论

0条评论

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