为啥元类不能访问由元类定义的类的子类继承的属性?

Posted

技术标签:

【中文标题】为啥元类不能访问由元类定义的类的子类继承的属性?【英文标题】:Why does a metaclass not have access to the attributes inhereited from a subclass of a class defined by the metaclass?为什么元类不能访问由元类定义的类的子类继承的属性? 【发布时间】:2015-07-06 16:40:59 【问题描述】:

Foo 使用元类 Meta 定义。元类循环遍历类属性并将它们打印到屏幕上。

Bar 子类Foo。但是,元类不会打印从 Bar 继承的属性。

为什么元类不能访问FooBar 中继承的属性?我对 python 的元类系统有什么不了解的地方?

这是2.7中的示例代码:

class Meta(type):
    def __init__(cls, name, bases, attrs):
        print "bases = ".format(bases)
        items = k:v for k,v in attrs.iteritems() if not k.startswith('__')
        for k,v in items.iteritems():
            print k, v

class Foo(object):
    __metaclass__ = Meta
    hi = 1

# This prints:
# bases = (<type 'object'>,)
# hi 1

class Bar(Foo):
    pass

# This prints:
# bases = (<class '__main__.Foo'>,)

Foo.hi
#prints 1
Bar.hi
#prints 1

【问题讨论】:

【参考方案1】:

__init__attrs 参数仅包含 该类 的属性,而不包含其基类的属性。

Bar 对象没有hi 属性。相反,当您请求Bar.hi 时,查找将从Bar 开始,发现它没有hi,然后在基础Foo 中查找它。

【讨论】:

【参考方案2】:

正如@orlp 所说,attrs 仅包含正在创建的类的类字典。但是,您仍然可以访问hi,因为它位于Foo 的基础之一的__dict__ 属性中。也就是说,您可以做一些与您所拥有的类似的事情,但通过基类递归并打印出每个基类字典中的条目。

另一种方法是使用dir(),它应该大致返回一个类所有属性的列表。我说大致是因为一个类可以实现__getattr____getattribute__ 以“即时”返回属性,这意味着该类可能没有为dir() 定义明确的属性集返回 - 请参阅完整的免责声明 here。但在许多常见情况下,类似以下的方法会起作用:

class Meta(type):
    def __init__(cls, name, bases, attrs):
        print "bases = ".format(bases)
        for attr in dir(cls):
            if not attr.startswith('_'):
                print attr, getattr(cls, attr)

class Foo(object):
    __metaclass__ = Meta
    hi = 1

class Bar(Foo):
    pass

哪些打印:

bases = (<type 'object'>,)
hi 1
bases = (<class '__main__.Foo'>,)
hi 1

【讨论】:

以上是关于为啥元类不能访问由元类定义的类的子类继承的属性?的主要内容,如果未能解决你的问题,请参考以下文章

派生类的类/元类方法装饰器

在继承元类的类中调用 super 会引发错误

避免使用元类继承生成的类属性

元类如何访问子类上定义的属性?

为啥不在实例属性查找中搜索元类的属性?

面向对象之元类