metaClass 简介

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
  • 每个变量都有自己的类型
  • 变量的类型是可以随时改变的
  • type 只会返回对象的直接 type,就是定义该对象的类
  • 类的 type 还是 type,这说明 type 定义了 python 中所有类的某些基本属性
(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  # >
print foo.magic() # 1

type 的三个参数分别是:

  • name: 要生产的类名
  • bases:包含所有基类的 tuple
  • dict:类的所有属性,是键值对的字典
    现在再回顾一下 “python 中一切皆对象”这句话,可能会更容易理解。

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  
__call__ *args= (1, 2)
MyKlass object with a=1, b=2
5.总结
  • 系统默认的type 作为__metaclass__,我们可以覆盖这个值。
  • 我们可以覆盖def __new__(cls, name, bases, attrs):def __init__(cls, name, bases, attrs):自定义行为,如单例模式的控制

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%(delim)s) |   # Escape sequence of two delimiters
      (?P%(id)s)      |   # delimiter and a Python identifier
      {(?P%(id)s)}   |   # delimiter and a braced identifier
      (?P)              # 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

你可能感兴趣的:(metaClass 简介)