我们可以使用super()来测试MRO中类方法之间的身份吗?

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

考虑下面的例子。

class A:
 def m():
  pass

class B(A):
 pass

和下面的终端输出。

>>> b = B()
>>> b.m
<bound method A.m of <__main__.B object at 0x000001EFFF24C748>>
>>> super(b.__class__, b).m
<bound method A.m of <__main__.B object at 0x000001EFFF24C748>>
>>> b.m is super(b.__class__, b).m
False
>>> b.m == super(b.__class__, b).m
True

为什么它们是等价的而不是相同的?继承方法时,是否会对方法进行复制?

是否有更好的方法来测试子类是否重写了父类方法?

python inheritance
2个回答
2
投票

你可以使用 __dict__ 属性来检查哪些方法和属性被覆盖了。

>>> class A:
...     def m():
...         pass
...
>>> class B(A):
...     pass
...
>>> class C(A):
...     def m():
...         pass
...
>>> 'm' in A.__dict__
True
>>> 'm' in B.__dict__
False                    # not overridden
>>> 'm' in C.__dict__
True                     # overridden

1
投票

使用 super(b.__class__, b) 产生一个实现 __getattr__ 的方法,将上升到 __mro__ 属性,从位置1开始(跳过当前类),寻找第一个具有指定属性的类。然后它将返回该绑定方法。更好的解释请看 本回答.

知道所有的函数也都是描述符,如下所示。

class A:
 def m(self):
  pass

创建一个对象 A 带属性的 m 这将是一个函数和描述符。当你初始化一个对象 a 级别的 A,基本上会导致 a.m = A.m.__get__(a) 它产生的约束方法具有 a 作为第一参数 self.

现在由于 super 也会检索绑定的方法,检查的是两个实例之间的身份。A.m.__get__(a) 产生你的终端输出。

>>> A.m.__get__(a)
<bound method A.m of <__main__.A object at 0x...>>
>>> A.m.__get__(a) is A.m.__get__(a)
False

所以两次调用类描述符 m 产生不同的绑定实例,这就是为什么身份检查失败的原因。相反,你应该测试产生绑定方法的函数的身份。幸运的是,一个绑定方法包含了 __func__ 属性,返回原始函数。所以,如果要查询任何一个实例的类是否覆盖了一个继承的函数,而不需要知道更多的实例和函数的名称,你可以这样做。

>>> a.__class__.m is super(a.__class__, a).m.__func__
True
© www.soinside.com 2019 - 2024. All rights reserved.