metaclass
译为元类
,可以理解为 metaclass 为描述类的超类,同时可以改变子类的形态,本文介绍相关内容。
Python中的类 {#Python中的类}
- class,对于大部分面向对象语言来说,class是一段定义了如何产生object的代码块。在Python中这一定义也成立。
- 但是在Python中,class并不只有这一角色。class实际上也是object。当我们使用class定义一个类的时候,Python会执行相应代码并在内存中创建一个对应类的的object。
- 而metaclass 是控制类实例(这里表示类本身的定义,不是类的实例)产生的"超类"
直接看一个 metaclass的例子可以有更直观的理解
metaclass 实例 {#metaclass-实例}
- 输出
- 从上面的运行结果可以发现在定义 class Foo() 定义时,会依次调用 MyMeta 的
__new__
和__init__
方法构建 Foo 类,然后在调用 foo = Foo() 创建类的实例对象时,才会调用 MyMeta 的__call__
方法来调用 Foo 类的__new__
和__init__
方法。
在没有 metaclass 的情况下,子类继承父类,父类是无法对子类执行操作的,但有了 metaclass,就可以对子类进行操作,就像装饰器那样可以动态定制和修改被装饰的类,metaclass 可以动态的定制或修改继承它的子类。
meatclass 工作原理 {#meatclass-工作原理}
类是 type 类的实例 {#类是-type-类的实例}
- 输出
- 可以看到类是 type 类的实例
用户定义类,是 type 类的 __call__
运算符重载 {#用户定义类,是-type-类的-call-运算符重载}
当我们定义一个类的语句结束时,真正发生的情况,是 Python 调用 type 的 __call__
运算符。
当你定义一个类时,写成下面这样时:
Python 真正执行的是下面这段代码:
这里等号右边的 type(classname, superclasses, attributedict),就是 type 的 __call__
运算符重载,它会进一步调用:
metaclass 是 type 的子类,通过替换 type 的 __call__
运算符重载机制,"超越变形"正常的类 {#metaclass-是-type-的子类,通过替换-type-的-call-运算符重载机制,“超越变形”正常的类}
正是 Python 的类创建机制,给了 metaclass 大展身手的机会。一旦你把一个类型 MyClass 的 metaclass 设置成 MyMeta,MyClass 就不再由原生的 type 创建,而是会调用 MyMeta 的 __call__
运算符重载。
metaclass 使用方法 {#metaclass-使用方法}
定义属性 {#定义属性}
当定义class的时候,我们可以使用__metaclass__
attribute来指定用来初始化当前class的metaclass。如下面的例子所示:
-
如果我们指定了
__metaclass__
,Python就是使用这个metaclass来生成classFoo
。 -
当Python试图创建class
Foo
的时候,Python会首先在class的定义中寻找__metaclass__
attribute。如果存在__metaclass__
,Python将会使用指定的__metaclass__
来创建classFoo
。如果没有指定的话,Python就会使用默认的type
作为metaclass创建Foo
。
显式指定 metaclass {#显式指定-metaclass}
在创建类时显式指定 metaclass 类的内容
-
该方法指定的metaclass会被子类继承
-
也就是说指定 metaclass 之后,Foo 类及其子类在默认情况下都会使用 Mymeta 作为 metaclass
-
这就是控制子类行为的方法
继承父类 metaclass {#继承父类-metaclass}
对于下面这个例子:
-
Python首先在
Foo
中寻找是否存在__metaclass__
attribute。 -
如果存在的话,Python将使用这个metaclass在内存中创建一个名字为
Foo
的class object。如果Python -
如果class定义中不存在
__metaclass__
的话,Python将会寻找MODULE级别的__metaclass__
。如果存在的话就进行与前述相同的操作。 -
当Python仍然没有找到
__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-的风险}
不过,凡事有利必有弊,尤其是 metaclass 这样"逆天"的存在。正如你所看到的那样,metaclass 会"扭曲变形"正常的 Python 类型模型。所以,如果使用不慎,对于整个代码库造成的风险是不可估量的。
换句话说,metaclass 仅仅是给小部分 Python 开发者,在开发框架层面的 Python 库时使用的。而在应用层,metaclass 往往不是很好的选择。
参考资料 {#参考资料}
文章链接:
https://www.zywvvd.com/notes/coding/python/python-metaclass/python-metaclass/