专栏首页醉梦轩Python中实现单例的N种方法

Python中实现单例的N种方法

0x00 前言

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

0x01 基本法

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__方法来实现。

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,拥有创造类的能力,因此成为元类

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()

输出如下内容:

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 装饰器法

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

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__函数可以改变返回实例的特点做到这一点。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • python metaclass 详细说明

    在理解metaclass之前,我们需要先理解Python中的class。从某种程度上来说,Python中的class的定位比较特殊。

    用户5760343
  • python 装饰器写在类中

    class A: # Decorator as an instance method def decorator1(self, func): @wraps...

    用户5760343
  • shiro教程2(自定义Realm)

    通过shiro教程1我们发现仅仅将数据源信息定义在ini文件中与我们实际开发环境有很大不兼容,所以我们希望能够自定义Realm。

    奋斗蒙
  • Golang面向对象编程之构造函数【struct&new】

    构造函数是一种特殊的方法,主要用来在创建对象时初始化对象,即为对象成员变量赋初始值。特别的一个类可以有多个构造函数 ,可根据其参数个数的不同或参数类型的不同来区...

    Allen.Wu
  • Java基础系列7——集合系列(1)框架概述

    该系列博文会告诉你如何从入门到进阶,一步步地学习Java基础知识,并上手进行实战,接着了解每个Java知识点背后的实现原理,更完整地了解整个Java技术体系,形...

    用代码征服天下
  • 继承、派生、新式类与经典类

    Downwards is the only way forwards .——《盗梦空间》

    用户6817597
  • Golang面向对象编程之继承&虚基类【组合&接口】

    Golang里面没有像C++一样有继承相关的概念,但是我们却可以实现继承相关的用法,这就要用到struct、interface这两个结构。

    Allen.Wu
  • orm

    对象关系映射(Object Relational Mapping,简称ORM)是通过使用描述对象和数据库之间映射的元数据,将面向对象语言程序中的对象自动持久化到...

    用户6817597
  • 前端入门系列之CSS

    CSS (Cascading Style Sheets) 是用来样式化和排版你的网页的 —— 例如更改网页内容的字体、颜色、大小和间距,将内容分割成多列或者加入...

    王大锤
  • 组合、封装、多态

    继承:一种类与类的关系,一种什么是什么的关系,子类是父类的从属关系。 组合:对象与对象的关系,一种什么有什么的关系,一个对象拥有另一个对象。 组合优点:让类...

    用户6817597

扫码关注云+社区

领取腾讯云代金券