考虑下面的例子。
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
为什么它们是等价的而不是相同的?继承方法时,是否会对方法进行复制?
是否有更好的方法来测试子类是否重写了父类方法?
你可以使用 __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
使用 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