| 注册
请输入搜索内容

热门搜索

Java Linux MySQL PHP JavaScript Hibernate jQuery Nginx
jopen
9年前发布

python单例模式与metaclass

单例模式的实现方式

将类实例绑定到类变量上

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 typetype(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',) {}

</div>

元类的 __init__ 和 __new__ 只在创建类 Foo 调用了一次,而创建 Foo 的实例时,每次都会调用元类的 __call__ 方法

</div>

来自: http://segmentfault.com/a/1190000004278703

 本文由用户 jopen 自行上传分享,仅供网友学习交流。所有权归原作者,若您的权利被侵害,请联系管理员。
 转载本站原创文章,请注明出处,并保留原始链接、图片水印。
 本站是一个以用户分享为主的开源技术平台,欢迎各类分享!