Can we use super() to test identity between class methods in the MRO?

主宰稳场 提交于 2020-08-09 09:13:06

问题


Consider the following example:

class A:
 def m():
  pass

class B(A):
 pass

And the following terminal output:

>>> 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

Why are they equal but not identical? Is a copy of the method made when it is inherited?

Are there better ways to test whether a child class has overridden a parent method?


回答1:


You can use the __dict__ attribute to check which methods and attributes have been overridden:

>>> 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



回答2:


Using super(b.__class__, b) produces an object that implements a __getattr__ method that will go up the __mro__ attribute starting at position 1 (skipping the current class) and look for the first class that has the specified attribute. It will then return that bound method. For a better explanation see this answer.

Knowing that all functions are also descriptors the following

class A:
 def m(self):
  pass

creates an object A with the attribute m which will be a function and descriptor. When you initialize an object a of class A, it will basically result in a.m = A.m.__get__(a) which produces the bound method that has a as the first argument self.

Now since super also retrieves bound methods what is being checked is the identity between 2 instances of A.m.__get__(a) producing your terminal output:

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

So 2 calls to the class descriptor m produce different bound instances and it is why the identity check fails. Instead you should test the identity of the functions that produced the bound methods. Luckily a bound method contains the __func__ attribute that returns the original function. So to lookup whether any instance's class has overridden an inherited function without knowing more than just the instance and name of the function you can do:

>>> a.__class__.m is super(a.__class__, a).m.__func__
True


来源:https://stackoverflow.com/questions/62301590/can-we-use-super-to-test-identity-between-class-methods-in-the-mro

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!