Можем ли мы использовать super() для проверки идентичности между методами класса в MRO?

Рассмотрим следующий пример:

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

Почему они равны, но не идентичны? Делается ли копия метода при его наследовании?

Есть ли лучшие способы проверить, переопределил ли дочерний класс родительский метод?

2 ответа

Решение

С помощью 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

Итак, 2 вызова дескриптора класса mсоздают разные связанные экземпляры, и именно поэтому проверка личности не выполняется. Вместо этого вы должны проверить идентичность функций, которые создали связанные методы. К счастью, связанный метод содержит__func__атрибут, возвращающий исходную функцию. Итак, чтобы узнать, переопределил ли какой-либо класс экземпляра унаследованную функцию, не зная больше, чем просто экземпляр и имя функции, вы можете:

>>> a.__class__.m is super(a.__class__, a).m.__func__
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
Другие вопросы по тегам