前面的话:这两天终于硬着头皮把python中的元类看完了,理解的不是很深刻,就打算写篇笔记,在回顾一遍吧!~~
一、什么是元类(metaclass)
元类(metaclass)是用来创建类(对象)的可调用对象。这里的可调用对象可以是函数或者类等。但一般情况下,我们使用类作为元类。对于实例对象、类和元类,我们可以使用下面的图来描述。
我们可以使用type
来创建类(对象),事实上,type
就是一个元类。
元类的主要目的是为了控制类的创建行为。
二、元类的使用
先看一个简单的例子,假设有下面的类:
class Foo(object):
name = "foo"
def bar(self):
print("bar")
现在我们想给这个类的方法和属性名称前面加上my_
前缀,即就是把name
变成my_name
。另外,我们还要增加一个echo方法。这里我们通过元类实现。
- 首先,定义一个元类,按照默认习惯,类名以Metaclass结尾。
class PrefixMetaclass(type):
def __new__(cls, name, bases, attrs):
# 给所有的属性和方法前面加上前缀 my_
_attrs = (("my_" + name, value) for name, value in attrs.items())
# 转化为字典
_attrs = dict((name, value) for name, value in _attrs)
# 增加一个echo方法
_attrs["echo"] = lambda self, phrase: phrase
# 返回创建后的类
return type.__new__(cls, name, bases, _attrs)
# 注意点:PrefixMetaClass是从type继承的,这是因为PrefixMetaclass是用来定制类的。
# __new__是在__init__之前被调用的特殊方法,它用来创建对象并返回创建后的对象,各个参数说明如下:
# cls: 当前准备创建的类
# name: 类的名字
# bases: 类的父类集合
# attrs: 类的属性和方法,是一个字典。
接着,我们需要指示Foo使用PrefixMetaclass来定制类。
在Python2中,我们只需在Foo中加一个
__metaclass__
的属性,如下:class Foo(object): __metaclass = PrefixMetaclass name = "foo" def bar(self): print("bar")
在Python3中,我们这么做:
class Foo(metaclass = PrefixMetaclass): name = "foo" def bar(self): print("bar")
现在,让我们来看看使用的结果。
可以看到,Foo原来的属性name已经变成了my_name
,而方法bar也变成了my_bar
这就是元类的魔法。
下面我们再来看一个继承的例子,下面是完整的代码。
class PrefixMetaclass(type):
def __new__(cls, name, bases, attrs):
_attrs = (("my_" + name, value) for name, value in attrs.items())
_attrs = dict((name, value) for name, value in _attrs)
_attrs["echo"] = lambda self, phrase: phrase
return type.__new__(cls, name, bases, _attrs)
class Foo(metaclass=PrefixMetaclass):
name = "foo"
def bar(self):
print("bar")
class Bar(Foo):
prop = "bar"
b = Bar()
print(b.my_prop)
print(b.my_name)
b.my_bar()
print(b.echo("hello"))
我们发现,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来创建它。
参考资料
https://stackoverflow.com/questions/100003/what-are-metaclasses-in-python
http://funhacks.net/explore-python/Class/metaclass.html
http://blog.jobbole.com/21351/