python实现单例模式的四种方式及相关知识解释

python实现单例模式的四种方式及相关知识解释

  • 模块模式
  • 装饰器模式
  • 父类重写new继承
  • 元类重写call

单例模式作为最常用的设计模式,在面试中很可能遇到要求手写.从最近的学习python的经验而言,singleton实现的四种方法都是python的重要特征,反过来也刚好是几种特征的最佳实现.(比如你平常开发中很难遇到几个需要写元类的地方)如果不能随手写出某种实现,说明你对于那种实现的概念还没有完全掌握.最近场通过写装饰器模式的singleton来复习装饰器概念.

1. module实现

#模块实现
from Singleton import instance

instance
<Singleton.Singleton at 0x19cf6691240>
#Singleton.py
class Singleton(object):
    pass

instance = Singleton()

知识点关联
python导入模块时即将模块运行一遍,可定义__all__变量指定那些变量可以被外部引用

2. 装饰器实现

#decorator装饰器实现
def Singleton(cls):
    #instance作为字典变量写在外部,可以保证该装饰器的重用性
    _instances = {}
    def getinstance(*arg,**kwarg):
        if cls not in _instances:
            _instances[cls] = cls(*arg,**kwarg)
        return _instances[cls]
    return getinstance
@Singleton
class A(object):
    pass
a = A()
b = A()
print(id(a))
print(id(b))
2268820031976
2268820031976
a is b
True
A.__name__
'getinstance'

从学习过程来看,装饰器学习的要点在于理解at语法等于一次A = Singleton(A).装饰过后A.__name__查看可以发现已经变成getinstance,在装饰器的getinstance之前使用functool.wrap可以解决问题.

3. 父类继承实现

#父类重写__new__
class Singleton(object):
    _instance = None
    def __new__(cls,*arg,**kwarg):
        if not cls._instance:
            cls._instance = super(Singleton,cls).__new__(cls, *arg, **kwarg)
        return cls._instance
class A(Singleton):
    pass
a = A()
b = A()
print(id(a))
print(id(b))
a is b
2179890302760
2179890302760
True

通过这个例子可以了解到父类都可以做些什么,无非是提前定义变量和函数.通过重写__new__函数,改变了A类别创建实例的过程.
推广开来的话,基本上本文的四个例子都是在”改变创建实例的过程”.包括JAVA中实现单例的几种方法也是同个道理.记住了这一点,知识便有了线索,就算下次忘了细节,也能沿着线索重新回忆所学过的内容.
将程序设计的思想应用到学习过程中,从例子中抽象得出原理,可以提高学习效率.

4. 元类实现

#使用元类
class Singleton(type):
    _instance = None #此处可改为_instances = {}字典实现
    def __call__(cls,*arg,**kwarg):
        if not cls._instance:#条件相应得改为 not cls in _instances
            cls._instance = cls.__new__(cls,*arg,**kwarg)
            #若instances为字典,则_instances[cls] = cls.__new__(cls,*arg,**kwarg)
        return cls._instance
class A(object,metaclass=Singleton):
    pass
a = A()
b = A()
print(id(a))
print(id(b))
a is b
1627904503936
1627904503936 
True

到了最烦人的metaclass方式,总的来说逻辑是一样的,就是拦截new一个实例的过程,判断是否已经创建过实例,保存已创建的实例放在要进行单例设计的类的一层(即作为一个类变量),直接返回已创建过的实例即可.
本例子中__call__实际上是一个实例方法,而相对于类的实例,元类的实例就是类(抽象层次上:元类>类>实例,这一层具体概念待以后起文章补充),因此通过元类Singleton创建出来的class A.定义了__call__实际上就是定义了’A()’时应该执行的操作.(实在有点拗口,理科生的表述能力希望各位谅解)
如同在下面的实例中,定义了__call__方法实际上是定义了’bb()’时的操作.个人认为这样的使用方法确实很不直观,起码我认为 __call__ 方法创建的目的不是让我们这么用的.如果非要继承,也几乎找不到理由不使用父类继承的方法替代元类实现.

class B(object):
    def __call__(self):
        print(self,' is calling!')
bb = B()
bb()
<__main__.B object at 0x0000017B06AEBCF8>  is calling!

如果你对metaclass仅仅是了解,建议从ORM框架学习,能够对metaclass有一个更加实际合理的概念.
另外,在元类实现当中,网上有其他文章使用了_instance = {}字典作为instance的类型,起初以为是为了重用性,但是下面的实际例子中好像class C的使用也没有其他问题.如果有潜在的问题欢迎指出.

class C(object,metaclass=Singleton):
    pass
cc = C()
dd = C()
print(id(cc))
print(id(dd))
cc is dd
1627905621184
1627905621184
True

猜你喜欢

转载自blog.csdn.net/m0_37422289/article/details/79200393