深入理解Python中的metaclass

本文介绍了Python中的元类(metaclass),元类是用于创建类的对象,主要目的是控制类的创建行为。通过元类,我们可以定制类的属性和方法,如在类定义中自动添加前缀。文章通过实例展示了如何使用元类以及元类的继承特性,并总结了元类的三大作用:拦截类创建、修改类定义、返回修改后的类。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前面的话:这两天终于硬着头皮把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/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值