metaClass 简介

2017-04-12  本文已影响15人  lmem

1.python 中类型

(1) type 判断类型

python是一种动态类型语言,换句话说每个变量可以在程序里任何地方改变它的类型。想要获取变量的类型信息,可以使用type

>>> a = 2
>>> type(a)
int

>>> a = '1'
>>> type(a)
str

>>> type(str)
type
>>> type(type)
type

>>> class Test1 (object):
        pass
>>> class Test2 (Test1):
        pass

>>> a = Test1()
>>> b = Test2()

>>> type(a) is Test1
True
>>> type(b) is Test2
True
(2) type 创建类型
Help on class type in module __builtin__:

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

类的创建

class Foo(object):
    def __init__(self):
        self.a = 1
    
    def magic(self):
        return self.a

#第二种创建方式
def __init__(self):
    self.a = 1
    
def magic(self):
    return self.a
    
Foo = type('Foo', (object,), {"__doc__": "A class that does nothing.", "__init__": __init__, "magic": magic})

foo = Foo()
print foo
print foo.a  # 1
print foo.magic  # <bound method Foo.magic of <__main__.Foo object at 0x100fa5d50>>
print foo.magic() # 1

type 的三个参数分别是:

2.metaclass 就是类的类

我们在前面看到怎么使用 type 来动态创建类,其实在 python 内部也进行着同样的步骤。这就是 metaclass 的概念!
想弄明白 metaclass,我们要搞清楚 class。因为类似于 class定义了 instance 的行为, metaclass 则定义了 class 的行为。可以说, classmetaclassinstance

想弄明白 metaclass,我们要搞清楚 class。因为类似于 class 定义了 instance 的行为, metaclass则定义了 class的行为。可以说, classmetaclassinstance
类的创建过程

class MyClass(object):
    pass

不会立即去创建这个类,而是把这段代码当做正常的code block来执行,结果就是生成一个命名空间(namespace),就是包含了要生成类(class-to-be)所有属性的字典,然后才会调用 __new__函数,把类名、类的父类、类的属性传递过去,生成这个类。

3.自定义metaclass

继承 type 这个类,覆盖已有的内置函数,就可以创建自己的 metaclsss
下面的内容都会基于一个这样的并metaclass:它为要创建的类自动添加一个属性 __cizixs

class MyMetaclass(type):
    def __init__(cls, name, bases, attrs):
        cls.__cizixs = "Don't panic"
        print("In MyMetaclass for {}".format(name))
        super(MyMetaClass, cls).__init__(name, bases, attrs)

其实还可以覆写 __new__ 函数来达到相同的目的:

class MyMetaclass(type):
    def __new__(cls, name, bases, attrs):
        attrs['__cizixs'] = "Don't panic"
        print("In MyMetaclass for {}".format(name))
        return super(MyMetaclss, cls).__new__(cls, name, bases, attrs)

class Foo(object):
    __metaclass__ = MyMetaclass
    
    pass
    
#  In MyMetaclass for Foo

foo = Foo()
print foo.__cizixs  # Don't panic

4.__call__

__call__is called when the already-created class is "called" to instantiate a new object

class MyMeta(type):
    def __call__(cls, *args, **kwds):
        print '__call__ of ', str(cls)
        print '__call__ *args=', str(args)
        return type.__call__(cls, *args, **kwds)

class MyKlass(object):
    __metaclass__ = MyMeta

    def __init__(self, a, b):
        print 'MyKlass object with a=%s, b=%s' % (a, b)

print 'gonna create foo now...'
foo = MyKlass(1, 2)

打印

gonna create foo now...
__call__ of  <class '__main__.MyKlass'>
__call__ *args= (1, 2)
MyKlass object with a=1, b=2
5.总结

6.demo

(1)string.Template

class Template:
    """A string class for supporting $-substitutions."""
    __metaclass__ = _TemplateMetaclass

    delimiter = '$'
    idpattern = r'[_a-z][_a-z0-9]*'

    def __init__(self, template):
        self.template = template
class _TemplateMetaclass(type):
    pattern = r"""
    %(delim)s(?:
      (?P<escaped>%(delim)s) |   # Escape sequence of two delimiters
      (?P<named>%(id)s)      |   # delimiter and a Python identifier
      {(?P<braced>%(id)s)}   |   # delimiter and a braced identifier
      (?P<invalid>)              # Other ill-formed delimiter exprs
    )
    """

    def __init__(cls, name, bases, dct):
        super(_TemplateMetaclass, cls).__init__(name, bases, dct)
        if 'pattern' in dct:
            pattern = cls.pattern
        else:
            pattern = _TemplateMetaclass.pattern % {
                'delim' : _re.escape(cls.delimiter),
                'id'    : cls.idpattern,
                }
        cls.pattern = _re.compile(pattern, _re.IGNORECASE | _re.VERBOSE)

使用

>>> from string import Template
>>> Template("$name is $value").substitute(name='me', value='2')
'me is 2'

With a metaclass, the pattern class attribute is getting created just once when the module is being loaded and the class Template (or its subclass) definition is being executed. This saves time when Template objects are created, and makes sense because at class creation time we have all the information we need to compile the regex - so why delay this operation?
【1】http://eli.thegreenplace.net/2011/08/14/python-metaclasses-by-example

上一篇 下一篇

猜你喜欢

热点阅读