При вызове super() в производном классе я могу передать сам.__ class__?

Недавно я обнаружил (через StackOverflow), что для вызова метода в базовом классе я должен позвонить:

super([[derived class]], self).[[base class method]]()

Это прекрасно, это работает. Однако я часто копирую и вставляя между классами, когда я вношу изменения, и часто забываю исправить аргумент производного класса функции super().

Я хотел бы избежать необходимости запоминать аргумент производного класса. Могу ли я вместо этого использовать self.__class__ в качестве первого аргумента функции super()?

Кажется, что это работает, но есть ли веские причины, почему я не должен этого делать?

Ответ 1

Нет, вы не можете. Вызов super() должен знать, к какому классу относится этот метод, для поиска базовых классов для переопределенного метода.

Если вы перейдете в self.__class__ (или еще лучше, type(self)), то super() задана неправильная стартовая точка для поиска методов и в конечном итоге вызовет свой собственный метод снова.

См. это как указатель в списке классов, которые формируют последовательность порядка разрешения метода. Если вы пройдете в type(self), тогда указатель будет ссылаться на любые подклассы вместо исходной начальной точки.

Следующий код приводит к ошибке бесконечной рекурсии:

class Base(object):
    def method(self):
        print 'original'

class Derived(Base):
    def method(self):
        print 'derived'
        super(type(self), self).method()

class Subclass(Derived):
    def method(self):
        print 'subclass of derived'
        super(Subclass, self).method()

Демо:

>>> Subclass().method()
subclass of derived
derived
derived
derived

<... *many* lines removed ...>

  File "<stdin>", line 4, in method
  File "<stdin>", line 4, in method
  File "<stdin>", line 4, in method
RuntimeError: maximum recursion depth exceeded while calling a Python object

потому что type(self) Subclass, не Derived, в Derived.method().

В этом примере MRO для Subclass составляет [Subclass, Derived, Base], а super() должен знать, с чего начать поиск любых переопределенных методов. Используя type(self), вы сообщите ему, чтобы он начинался с Subclass, поэтому он найдет следующий Derived.method(), с которого мы начали.

Ответ 2

self.__class__ может не быть подклассом, а скорее классом внука или младшего, что приводит к циклу разбиения стека.