python 中的元类

看一个项目的源码的时候,看到了这句,class xxx(six.with_metaclass(ABCMeta, xxx)):对其中的with_metaclass不甚理解,学习一番后,原来是涉及到了python中的高级知识:元类,学习后遂作记录。

什么是元类

首先说,元类是创建类的类。python 中一切皆对象,那么类同样也是对象,可以说,类是其元类的实例对象。所有元类的祖宗是 type,只有 type 及其子类才能作为元类。要想看一个对象的元类,可以通过 type()obj.__class__ 查看:
python 中的元类_第1张图片

如何创建类

既然类是其元类的实例对象,那么类的创建除了写 class 关键字,还可以通过 meta(classname,(parentclass,),{attr}) 来实例化元类的方式创建。另外,如果一个类 A 由其元类 B 创建,类 C 继承类 A,那么类 C 也是由元类B创建。也就是说:某个类的元类为A,那么该类及其子类都是由元类A创建。而元类是继承 type 的,因此还可以通过 type(classname,(parentclass,),{attr}) 创建。总结以上可得,类的三种创建方法:

  1. 通过 class 关键字创建
  2. 通过 meta(classname,(parentclass,),{attr}) 创建
  3. 通过 type(classname,(parentclass,),{attr}) 创建

其中元组()中传递的是该类继承的父类,字典 {} 传递的是类的属性或方法
python 中的元类_第2张图片
以上可以看到 A,B,C 都是 class 类型。类A中通过 metaclass 指定了 MyType 是 A 的元类。

实例的构造过程

一个实例是怎么被类创建出来的呢?先说结论:

类到实例经历了2个过程:

  1. 执行类的 __new__ 方法,返回一个空的对象
  2. 执行类的 __init__ 方法,对对象进行参数的初始化,后返回该对象

因此可以说 __new__ 这个魔法方法是个构造方法,而__init__ 方法是个初始化方法。

另外看一下 __call__ 方法,一个对象要想是可调用的,必须实现 __call__ 方法,或者说实现了__call__ 方法的对象都是可调用的。如果一个对象中实现了__call__方法,那么这个实例就可以调用。
python 中的元类_第3张图片

但是要注意调用的时机。第一个 A() 只是实例化对象的操作,a() 才算是对实例的调用。
知道了以上3个魔法方法以后,再看看类到实例经历了那些过程。
python 中的元类_第4张图片

可以看到依次执行了 __new__ 方法,__init__ 方法和 __call__ 方法。其中,定义 class A 相当于实例化了 MyType,实例化A() 就相当于调用了 Mytype 实例(也就是类A),因此触发了 Mytype 的__call__方法。

如果 __new__ 方法,__init__ 方法不返回最终得到的类就是 NoneType,可以去掉 return 重写 __new__ 方法,__init__ 方法自行验证。

with_metaclass是什么意思

先说结论:with_metaclass 是个函数,返回临时类,这个临时类由元类创建,同时又是新类的父类。
这么比较难懂,看代码。
首先看下 with_metaclass 的源码,做了些什么事情。
python 中的元类_第5张图片

进一步地,代码可以简化为
python 中的元类_第6张图片

以上执行 __new__ 实际就是对相应的类实例化操作。可以看到with_metaclass函数最终就是返回了 metaclass 元类的实例(也就是类),并且名字叫 temporary_class,那么 metaclass 实例化是什么呢,也就是说 metaclass 执行 __new__ 返回什么呢?因此以上代码可以继续简化为
python 中的元类_第7张图片

使用 with_metaclass 创建类的代码如下
python 中的元类_第8张图片

如果将A 中调用 with_metaclass 的参数带入进去就是
python 中的元类_第9张图片
可以看到,其实就是使用 Mytype 元类创建了一个临时类 temporary_class,A 继承了 temporary_class,根据某个类的元类为A,那么该类及其子类都是由元类A创建,那么类A实际上也是由 Mytype 创建。这就是 with_metaclass 的作用。

那么使用 metaclass 和 with_metaclass 有什么区别?本质上没有什么区别,只是写法上稍有不同。
python 中的元类_第10张图片

元类有什么用

通常是对定义的类有特殊要求的时候会用到,常见于框架中。
比如要求类中的属性不能以 test 开头,就可以这样写。
python 中的元类_第11张图片
最后,ABCMeta 是抽象元类,他继承 type ,如果元类不指明继承自ABCMeta,就会默认继承 type,创造的就不是抽象类了(抽象类是不能实例化的)。然后 type 是继承 object 的,object 真的是一切对象的大 boss。

看下源码,以证以上。
python 中的元类_第12张图片

type 继承 object
python 中的元类_第13张图片
或者
python 中的元类_第14张图片

你可能感兴趣的:(python3,元类,python,metaclass,with_metaclass)