编程派微信号:codingpy
作者:Ethan。
原文链接为:https://funhacks.net/2016/11/12/metaclass/。
本文约 6500 字,加上代码,读完可能需要 17 分钟。
Python 中的元类(metaclass)是一个深度魔法,平时我们可能比较少接触到元类,本文将通过一些简单的例子来理解这个魔法。
类也是对象
在 Python 中,一切皆对象。字符串,列表,字典,函数是对象,类也是一个对象,因此你可以:
把类赋值给一个变量
把类作为函数参数进行传递
把类作为函数的返回值
在运行时动态地创建类
看一个简单的例子:
熟悉又陌生的 type
在日常使用中,我们经常使用 object
来派生一个类,事实上,在这种情况下,Python 解释器会调用 type
来创建类。
这里,出现了 type
,没错,是你知道的 type
,我们经常使用它来判断一个对象的类型,比如:
事实上,type
除了可以返回对象的类型,它还可以被用来动态地创建类(对象)。下面,我们看几个例子,来消化一下这句话。
使用 type
来创建类(对象)的方式如下:
type(类名, 父类的元组(针对继承的情况,可以为空),包含属性和方法的字典(名称和值))
最简单的情况
假设有下面的类:
现在,我们不使用 class
关键字来定义,而使用 type
,如下:
上面两种方式是等价的。我们看到,type
接收三个参数:
第 1 个参数是字符串 ‘Foo’,表示类名
第 2 个参数是元组 (object, ),表示所有的父类
第 3 个参数是字典,这里是一个空字典,表示没有定义属性和方法
在上面,我们使用 type()
创建了一个名为 Foo 的类,然后把它赋给了变量 Foo,我们当然可以把它赋给其他变量,但是,此刻没必要给自己找麻烦。
接着,我们看看使用:
有属性和方法的情况
假设有下面的类:
用 type
来创建这个类,如下:
上面两种方式的效果是一样的,看下使用:
继承的情况
再来看看继承的情况,假设有如下的父类:
我们用 Base 派生一个 Foo 类,如下:
改用 type
来创建,如下:
—-|—-
什么是元类(metaclass)
元类(metaclass)是用来创建类(对象)的可调用对象。这里的可调用对象可以是函数或者类等。但一般情况下,我们使用类作为元类。对于实例对象、类和元类,我们可以用下面的图来描述
我们在前面使用了 type
来创建类(对象),事实上,type
就是一个元类。
那么,元类到底有什么用呢?要你何用…
元类的主要目的是为了控制类的创建行为。我们还是先来看看一些例子,以消化这句话。
元类的使用
先从一个简单的例子开始,假设有下面的类:
现在我们想给这个类的方法和属性名称前面加上 my_
前缀,即 name 变成 my_name,bar 变成 my_bar,另外,我们还想加一个 echo 方法。当然,有很多种做法,这里展示用元类的做法。
1.首先,定义一个元类,按照默认习惯,类名以 Metaclass 结尾,代码如下:
上面的代码有几个需要注意的点:
PrefixMetaClass 从
type
继承,这是因为 PrefixMetaclass 是用来创建类的__new__
是在__init__
之前被调用的特殊方法,它用来创建对象并返回创建后的对象,对它的参数解释如下:
2.接着,我们需要指示 Foo 使用 PrefixMetaclass 来定制类。
在 Python2 中,我们只需在 Foo 中加一个 __metaclass__
的属性,如下:
在 Python3 中,这样做:
现在,让我们看看使用:
可以看到,Foo 原来的属性 name 已经变成了 my_name,而方法 bar 也变成了 my_bar,这就是元类的魔法。
再来看一个继承的例子,下面是完整的代码:
其中,PrefixMetaclass 和 Foo 跟前面的定义是一样的,只是新增了 Bar,它继承自 Foo。先让我们看看使用:
我们发现,Bar 没有 prop 这个属性,但是有 my_prop 这个属性,这是为什么呢?
原来,当我们定义 class Bar(Foo)
时,Python 会首先在当前类,即 Bar 中寻找__metaclass__
,如果没有找到,就会在父类 Foo 中寻找 __metaclass__
,如果找不到,就继续在 Foo的父类寻找,如此继续下去,如果在任何父类都找不到 __metaclass__
,就会到模块层次中寻找,如果还是找不到,就会用 type 来创建这个类。
这里,我们在 Foo 找到了 __metaclass__
,Python 会使用 PrefixMetaclass 来创建Bar,也就是说,元类会隐式地继承到子类,虽然没有显示地在子类使用 __metaclass__
,这也解释了为什么 Bar 的 prop属性被动态修改成了 my_prop。
写到这里,不知道你理解元类了没?希望理解了,如果没理解,就多看几遍吧~
小结
在 Python 中,类也是一个对象。
类创建实例,元类创建类。
当你创建类时,解释器会调用元类来生成它,定义一个继承自 object 的普通类意味着调用 type 来创建它。
参考资料
扫码关注编程派
获取最新教程及资源推送
↓↓↓ 点击阅读原文,查看更多Python教程
阅读原文:http://tluoyi.blog.163.com/blog/static/2672490212016101611100747/