详解python单例模式与metaclass

553次阅读  |  发布于6年以前

单例模式的实现方式

将类实例绑定到类变量上


    class Singleton(object):
      _instance = None

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

但是子类在继承后可以重写new以失去单例特性


    class D(Singleton):

      def __new__(cls, *args):
        return super(D, cls).__new__(cls, *args)

使用装饰器实现


    def singleton(_cls):
      inst = {}

      def getinstance(*args, **kwargs):
        if _cls not in inst:
          inst[_cls] = _cls(*args, **kwargs)
        return inst[_cls]
      return getinstance

    @singleton
    class MyClass(object):
      pass

问题是这样装饰以后返回的不是类而是函数,当然你可以singleton里定义一个类来解决问题,但这样就显得很麻烦了

使用metaclass,这个方式最推荐


    class Singleton(type):
      _inst = {}

      def __call__(cls, *args, **kwargs):
        if cls not in cls._inst:
          cls._inst[cls] = super(Singleton, cls).__call__(*args)
        return cls._inst[cls]


    class MyClass(object):
      __metaclass__ = Singleton

metaclass

元类就是用来创建类的东西,可以简单把元类称为"类工厂",类是元类的实例。type就是Python的内建元类,type也是自己的元类,任何一个类


    >>> type(MyClass)
    type
    >>> type(type)
    type

python在创建类MyClass的过程中,会在类的定义中寻找metaclass,如果存在则用其创建类MyClass,否则使用内建的type来创建类。对于类有继承的情况,如果当前类没有找到,会继续在父类中寻找metaclass,直到所有父类中都没有找到才使用type创建类。
如果模块里有metaclass的全局变量的话,其中的类都将以其为元类,亲自试了,没这个作用,无任何影响

查看type的定义,

type(object) -> the object's type
type(name, bases, dict) -> a new type

所以利用type定义一个类的元类,可以用函数返回一个上面第二种定义的对象,也可以继承type并重写其中的方法。

直接使用type生成的对象作为元类,函数作用是使属性变为大写


    def update_(name, bases, dct):
      attrs = ((name, value) for name, value in dct.items() if not name.startswith('__'))
      uppercase_attr = {name.upper(): value for name, value in attrs}
      return type(name, bases, uppercase_attr)


    class Singleton(object):
      __metaclass__ = update_
      abc = 2

    d = Singleton()
    print d.ABC
    # 2

上一节中,单例模式元类实现用的是类继承方式,而对于第一种new的方式,本质上调用的是type.new,不过使用super能使继承更清晰一些并避免一些问题

这里简单说明一下,new是在init前调用的方法,会创建对象并返回,而init则是用传入的参数将对象初始化。看一下type中这两者以及call的实现


    def __init__(cls, what, bases=None, dict=None): # known special case of type.__init__
        """
        type(object) -> the object's type
        type(name, bases, dict) -> a new type
        # (copied from class doc)
        """
        pass

    @staticmethod # known case of __new__
    def __new__(S, *more): # real signature unknown; restored from __doc__
      """ T.__new__(S, ...) -> a new object with type S, a subtype of T """
      pass

    def __call__(self, *more): # real signature unknown; restored from __doc__
      """ x.__call__(...) <==> x(...) """
      pass

前面提到类相当于元类的实例化,再联系创建单例模式时使用的函数,用的是call,其实用三种magic method中任何一种都是可以的,来看一下使用元类时各方法的调用情况


    class Basic(type):
      def __new__(cls, name, bases, newattrs):
        print "new: %r %r %r %r" % (cls, name, bases, newattrs)
        return super(Basic, cls).__new__(cls, name, bases, newattrs)

      def __call__(self, *args):
        print "call: %r %r" % (self, args)
        return super(Basic, self).__call__(*args)

      def __init__(cls, name, bases, newattrs):
        print "init: %r %r %r %r" % (cls, name, bases, newattrs)
        super(Basic, cls).__init__(name, bases, dict)


    class Foo:
      __metaclass__ = Basic

      def __init__(self, *args, **kw):
        print "init: %r %r %r" % (self, args, kw)

    a = Foo('a')
    b = Foo('b')

结果


    new: <class '__main__.Basic'> 'Foo' () {'__module__': '__main__', '__metaclass__': <class '__main__.Basic'>, '__init__': <function init at 0x106fd5320>}
    init: <class '__main__.Foo'> 'Foo' () {'__module__': '__main__', '__metaclass__': <class '__main__.Basic'>, '__init__': <function init at 0x106fd5320>}
    call: <class '__main__.Foo'> ('a',)
    init: <__main__.Foo object at 0x106fee990> ('a',) {}
    call: <class '__main__.Foo'> ('b',)
    init: <__main__.Foo object at 0x106feea50> ('b',) {}

元类的initnew只在创建类Foo调用了一次,而创建Foo的实例时,每次都会调用元类的call方法

以上就是本文的全部内容,对python单例模式与metaclass进行了描述,希望对大家的学习有所帮助。

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8