python - 在类的类方法中调用 super() 以获取元类方法

标签 python python-3.x metaclass

就在我理解元类的时候...

免责声明:我在发帖前四处寻找答案,但我找到的大部分答案都是关于调用 super() 的去另一个@classmethod在 MRO 中(不涉及元类),或者,令人惊讶的是,他们中的很多人都试图在 metaclass.__new__ 中做一些事情。或 metaclass.__call__这意味着该类尚未完全创建。我很确定(假设是 97%)这不是其中一个问题。


环境:Python 3.7.2


问题: 我有一个元类 FooMeta定义了一个方法 get_foo(cls) , 一类 Foo它是从那个元类构建的(所以是 FooMeta 的一个实例)并且有一个 @classmethod get_bar(cls) .然后再上一节课Foo2继承自 Foo .在 Foo2 , 我子类 get_foo通过将其声明为 @classmethod并调用 super() .这惨败......

即使用此代码

class FooMeta(type):
    def get_foo(cls):
        return 5


class Foo(metaclass=FooMeta):
    @classmethod
    def get_bar(cls):
        return 3


print(Foo.get_foo)
# >>> <bound method FooMeta.get_foo of <class '__main__.Foo'>>
print(Foo.get_bar)
# >>> <bound method Foo.get_bar of <class '__main__.Foo'>>


class Foo2(Foo):
    @classmethod
    def get_foo(cls):
        print(cls.__mro__)
        # >>> (<class '__main__.Foo2'>, <class '__main__.Foo'>, <class 'object'>)
        return super().get_foo()

    @classmethod
    def get_bar(cls):
        return super().get_bar()


print(Foo2().get_bar())
# >>> 3
print(Foo2().get_foo())
# >>> AttributeError: 'super' object has no attribute 'get_foo'


问题: 因此,我的类是元类的一个实例,并且已验证这两个类方法都存在于类 Foo 中。 , 为什么不同时调用 super().get_***()在里面工作 Foo2我对元类或 super() 有什么不理解的?这让我觉得这些结果不合逻辑?

编辑:进一步测试表明 Foo2 上的方法作为类方法或实例方法不会改变结果。

编辑 2:感谢@chepner 的回答,我认为问题在于 super()正在返回一个表示 Foo 的 super 对象(这是用 super().__thisclass__ 验证的)我期待 super().get_foo()表现(甚至打电话)get_attr(Foo, 'get_foo')在幕后。好像不是……我还在想为什么,但是越来越清楚了:)

最佳答案

Foo 可能有一个 get_foo 方法,但是 super 并不是为了检查父类(super class)有哪些属性而设计的。 super 关心哪些属性源自在父类(super class)中。


要理解 super 的设计,请考虑以下多重继承层次结构:

class A:
    @classmethod
    def f(cls):
        return 1
class B(A):
    pass
class C(A):
    @classmethod
    def f(cls):
        return 2
class D(B, C):
    @classmethod
    def f(cls):
        return super().f() + 1

A、B、C、D都有一个f类方法,但是B的f是继承自A的。D的方法解析顺序,类检查的顺序属性查找,转到 (D, B, C, A, object)

super().f() + 1cls 的 MRO 中搜索 f 实现。它应该找到的是 C.f,但 B 具有继承的 f 实现,并且 B 在 MRO 中位于 C 之前。如果 super 获取 B.f,这将打破 C 覆盖 f 的尝试,在某些情况下通常称为“菱形继承(钻石问题)”。

super 不查看 B 有哪些属性,而是直接查看 B 的 __dict__,因此它只考虑 B 实际提供的属性B 的父类(super class)或元类。


现在,回到您的 get_foo/get_bar 情况。 get_bar 来自 Foo 本身,所以 super().get_bar() 找到 Foo.get_bar。但是,get_foo 不是由 Foo 提供的,而是由 FooMeta 元类提供的,并且没有 get_foo 的条目在 Foo.__dict__ 中。因此,super().get_foo() 什么也没找到。

关于python - 在类的类方法中调用 super() 以获取元类方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58703499/

相关文章:

python - 当列表索引超出范围时插入错误消息

python - 如何创建动态过滤器?

python - 运行时错误 : Attempted to use a closed Session in tflearn

python - sqlalchemy自动增量值作为外键mysql

Python:将函数参数的默认值传递给 *args 或 **kwargs

python - 1 个类继承 2 个不同的元类(abcmeta 和用户定义的元)

python - 通过 systemd 启动 Bottle Web 服务器?

Python3 int, long统一实现

python - 如何使用 aiohttp 确定每秒请求数?

python - 子类化装饰类