前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python中实现单例的N种方法

Python中实现单例的N种方法

作者头像
drunkdream
发布2020-01-02 15:52:01
7630
发布2020-01-02 15:52:01
举报
文章被收录于专栏:醉梦轩醉梦轩

0x00 前言

单例是一种很常见的设计模式,在Python中不同的实现方法差异也比较大。这里介绍一些不同的实现方法。

0x01 基本法

代码语言:javascript
复制
class MyClass(object):
    _instance = None

    @staticmethod
    def get_instance():
        if not MyClass._instance:
            MyClass._instance = MyClass()
        return MyClass._instance

inst = MyClass.get_instance()

这种方法是最简单的实现方法,但是需要使用者主动调用get_instance方法来获取实例,如果写成inst = MyClass()的话,就不会起到单例的作用了。

0x02 重载new大法

为了解决上面的问题,可以通过重载__new__方法来实现。

代码语言:javascript
复制
class MyClass(object):
    _instance = None

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

    def initialize(self, *args, **kwargs):
        pass

inst = MyClass()

类在实例化的时候,会先调用__new__方法,在这里可以修改返回的实例。但是,这种方法有一个问题,就是实例化的时候一定会调用__init__方法,因此会出现重复初始化的问题。这里改为使用initialize方法进行初始化,也就是说,使用者需要避免使用__init__进行初始化。

但是这种方法对用户不是透明的,体验上不是很好。

0x03 元类法

元类是一种特殊的类,它继承自type,拥有创造类的能力,因此成为元类

代码语言:javascript
复制
class SingletonMetaClass(type):

    def __init__(cls, *args, **kwargs):
        super(SingletonMetaClass, cls).__init__(*args, **kwargs)
        print('SingletonMetaClass __init__')

    def __call__(cls, *args, **kwargs):
        print('SingletonMetaClass __call__')
        if not hasattr(cls, '_instance'):
            cls._instance = super(SingletonMetaClass, cls).__call__(*args, **kwargs)
        print('SingletonMetaClass __call__ return')
        return cls._instance

class MyClass(object, metaclass=SingletonMetaClass):

    def __new__(cls, *args, **kwargs):
        print('MyClass __new__')
        return super(MyClass, cls).__new__(cls, *args, **kwargs)

    def __init__(self, *args, **kwargs):
        print('MyClass __init__')

inst = MyClass()

输出如下内容:

代码语言:javascript
复制
SingletonMetaClass __init__
SingletonMetaClass __call__
MyClass __new__
MyClass __init__
SingletonMetaClass __call__ return

可以看出,正是在元类的__call__中,创造了MyClass这个类。

这种方法一般用来是没什么问题的,但是有些情况下会报:TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases这个错误。这是因为基类中也使用了元类的原因,此时需要保证元类的继承关系,以避免元类冲突。

0x04 装饰器法

装饰器是一种常用的动态修改函数行为的方法,因此也可以用于实现单例。

代码语言:javascript
复制
class Singleton(object):
    '''singleton decorator
    '''

    def __init__(self, cls):
        self.__instance = None
        self.__cls = cls

    def __call__(self, *args, **kwargs):
        if not self.__instance:
            self.__instance = self.__cls(*args, **kwargs)
        return self.__instance

@Singleton
class MyClass(object):
    pass

相比其它方法,这种方法缺点更少,使用也更加灵活,不需要修改类的实现。

0x05 总结

为了实现通用的单例逻辑,主要思路就是修改类的实例化过程。

__new__方法、元类法装饰器法都是通过在实例化之前判断是否已经实例化,从而返回对应的实例,差别只是在于实现逻辑位于实例化的不同阶段。元类法装饰器法是通过在类实例化之前判断是否已经进行过实例化;而__new__方法是在已经进入实例化过程,但是尚未进到__init__过程,利用__new__函数可以改变返回实例的特点做到这一点。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-11-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0x00 前言
  • 0x01 基本法
  • 0x02 重载new大法
  • 0x03 元类法
    • 0x04 装饰器法
      • 0x05 总结
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档