在类的类方法内调用super()以获取元类方法

问题描述 投票:1回答:3

当我虽然了解元类...

Disclaimer:发布之前我一直在寻找答案,但是我发现的大多数答案都是关于调用super()以获得MRO中的另一个@classmethod(不涉及元类),或者令人惊讶的,很多人都想尝试在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().__thisclass__验证),并且我期望super().get_foo()表现良好(甚至呼叫] get_attr(Foo, 'get_foo')。似乎不是。。。我仍然想知道为什么,但是越来越清楚了:)

python python-3.x metaclass
3个回答
2
投票

Foo可能具有get_foo方法,但是super并非旨在检查超类具有的属性。 super关心超类中的哪些属性[[originate。


要了解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的尝试。

[而不是查看B具有什么属性,super直接在B的__dict__中查找,因此它仅考虑B实际提供的属性,而不是B的超类或元类。


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

0
投票
get_foo不是Foo的属性,而是type(Foo)的属性:

-1
投票
注1
© www.soinside.com 2019 - 2024. All rights reserved.