深入理解Python中的metaclass

匿名 (未验证) 提交于 2019-12-02 22:54:36

元类(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/

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!