当我虽然了解元类...
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')
。似乎不是。。。我仍然想知道为什么,但是越来越清楚了:)
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() + 1
在cls
的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()
一无所获。
get_foo
不是Foo
的属性,而是type(Foo)
的属性: