metaclass译为元类,可以理解为 metaclass 为描述类的超类,同时可以改变子类的形态,本文介绍相关内容。
直接看一个 metaclass的例子可以有更直观的理解
class Mymeta(type):
def __init__(self, name, bases, dic):
super().__init__(name, bases, dic)
print('Mymeta.__init__')
print(self.__name__)
print(dic)
print(self.yaml_tag)
def __new__(cls, *args, **kwargs):
print('Mymeta.__new__')
print(cls.__name__)
return type.__new__(cls, *args, **kwargs)
def __call__(cls, *args, **kwargs):
print('Mymeta.__call__')
obj = cls.__new__(cls)
cls.__init__(cls, *args, **kwargs)
return obj
class Foo(metaclass=Mymeta):
yaml_tag = '!Foo'
def __init__(self, name):
print('Foo.__init__')
self.name = name
def __new__(cls, *args, **kwargs):
print('Foo.__new__')
return object.__new__(cls)
if __name__ == '__main__':
foo = Foo('foo')
Mymeta.__new__
Mymeta
Mymeta.__init__
Foo
{'__module__': '__main__', '__qualname__': 'Foo', 'yaml_tag': '!Foo', '__init__': <function Foo.__init__ at 0x000002626354E550>, '__new__': <function Foo.__new__ at 0x000002626354E5E0>}
!Foo
Mymeta.__call__
Foo.__new__
Foo.__init____new__ 和 __init__ 方法构建 Foo 类,然后在调用 foo = Foo() 创建类的实例对象时,才会调用 MyMeta 的 __call__ 方法来调用 Foo 类的 __new__ 和 __init__ 方法。在没有 metaclass 的情况下,子类继承父类,父类是无法对子类执行操作的,但有了 metaclass,就可以对子类进行操作,就像装饰器那样可以动态定制和修改被装饰的类,metaclass 可以动态的定制或修改继承它的子类。
class TestClass:
pass
if __name__ == '__main__':
print(type(TestClass))
print(isinstance(TestClass, type))<class 'type'>
True__call__ 运算符重载当我们定义一个类的语句结束时,真正发生的情况,是 Python 调用 type 的 __call__ 运算符。
当你定义一个类时,写成下面这样时:
class MyClass:
data = 1Python 真正执行的是下面这段代码:
class = type(classname, superclasses, attributedict)这里等号右边的 type(classname, superclasses, attributedict),就是 type 的 __call__ 运算符重载,它会进一步调用:
type.__new__(typeclass, classname, superclasses, attributedict)
type.__init__(class, classname, superclasses, attributedict)__call__ 运算符重载机制,“超越变形”正常的类正是 Python 的类创建机制,给了 metaclass 大展身手的机会。一旦你把一个类型 MyClass 的 metaclass 设置成 MyMeta,MyClass 就不再由原生的 type 创建,而是会调用 MyMeta 的 __call__ 运算符重载。
class = type(classname, superclasses, attributedict)
# 变为了
class = MyMeta(classname, superclasses, attributedict)当定义class的时候,我们可以使用__metaclass__ attribute来指定用来初始化当前class的metaclass。如下面的例子所示:
class Foo(object):
__metaclass__ = something
[other statements...]__metaclass__,Python就是使用这个metaclass来生成class Foo。
Foo的时候,Python会首先在class的定义中寻找__metaclass__ attribute。如果存在__metaclass__,Python将会使用指定的__metaclass__来创建class Foo。如果没有指定的话,Python就会使用默认的type作为metaclass创建Foo。
在创建类时显式指定 metaclass 类的内容
class Foo(metaclass=Mymeta):
pass对于下面这个例子:
class Foo(Bar):
passFoo中寻找是否存在__metaclass__ attribute。
Foo的class object。如果Python
__metaclass__的话,Python将会寻找MODULE级别的__metaclass__。如果存在的话就进行与前述相同的操作。
__metaclass__时,Python将会使用当前类的父类的metaclass来创建当前类。在我们上面这个例子中,Python会使用Foo的父类Bar的metaclass来创建Foo的class object。
同时需要注意的是,在class中定义的
__metaclass__attribute并不会被子类继承。被子类继承的是母类的metaclass,也就是母类的.__class__attribute。比如上面的例子中,Bar.__class__将会被Foo继承。也就是说,如果Bar定义了一个__metaclass__attribute来使用type()创建Bar的class object(而非使用type.__new__()),那么Bar的子类,也就是Foo,并不会继承这一行为。
不过,凡事有利必有弊,尤其是 metaclass 这样“逆天”的存在。正如你所看到的那样,metaclass 会"扭曲变形"正常的 Python 类型模型。所以,如果使用不慎,对于整个代码库造成的风险是不可估量的。
换句话说,metaclass 仅仅是给小部分 Python 开发者,在开发框架层面的 Python 库时使用的。而在应用层,metaclass 往往不是很好的选择。