Второй параметр super()?

Мой коллега написал код, аналогичный следующему сегодня, попросил меня взглянуть, и мне потребовалось некоторое время, чтобы обнаружить ошибку:

class A():                                                                                         
    def __init__(self):                                                         
        print('A')                                                              

class B(A):                                                                     
    def __init__(self):                                                         
        super(B).__init__()                                               

b = B()

Проблема здесь в том, что нет параметра self для super() в конструкторе B. Меня удивило то, что в этом случае абсолютно ничего не происходит, т.е. Нет ошибки, ничего. Что содержит объект super, созданный super(B)? Как объект, он явно имеет конструктор, так что то, что вызывается, но как этот объект связан с B? В частности, почему этот допустимый код и где-то не выбрасывает исключение? Является ли super(B) объектом с некоторым фактическим использованием и что это будет?

Ответы

Ответ 1

Единственное, что вызывает все эти двусмысленности, заключается в том, что "почему obj = super(B).__init__() работает?". Это потому, что super(B).__self_class__ возвращает None, и в этом случае вы вызываете None objects '__init__, как показано ниже, который возвращает None:

In [40]: None.__init__()

Что касается остальных случаев, вы можете просто проверить разницу, вызвав основные атрибуты super в обоих случаях:

In [36]: class B(A):                                                                     
        def __init__(self):                                                         
                obj = super(B, self)
                print(obj.__class__)
                print(obj.__thisclass__)
                print(obj.__self_class__)
                print(obj.__self__)
   ....:         

In [37]: b = B()
<class 'super'>
<class '__main__.B'>
<class '__main__.B'>
<__main__.B object at 0x7f15a813f940>

In [38]: 

In [38]: class B(A):                                                                     
        def __init__(self):                                                         
                obj = super(B)
                print(obj.__class__)
                print(obj.__thisclass__)
                print(obj.__self_class__)
                print(obj.__self__)
   ....:         

In [39]: b = B()
<class 'super'>
<class '__main__.B'>
None
None

Для остальных вещей я рекомендую вам внимательно прочитать документацию. https://docs.python.org/3/library/functions.html#super и эта статья Раймонда Хеттингера https://rhettinger.wordpress.com/2011/05/26/super-considered-super/.

Кроме того, если вы хотите знать, почему super(B) не работает вне класса и вообще почему вызов super() без какого-либо аргумента работает внутри класса, вы можете прочитать этот всеобъемлющий ответ Martijn fooobar.com/questions/12584/....

Краткое описание решения:

Как упоминалось в комментариях @Nathan Vērzemnieks, вам нужно вызвать инициализатор один раз, чтобы получить объект super(). Причина заложена в магии нового объекта super, который объясняется в вышеупомянутых ссылках.

In [1]: class A:
   ...:     def __init__(self):
   ...:         print("finally!")
   ...:

In [2]: class B(A):
   ...:     def __init__(self):
   ...:         sup = super(B)
   ...:         print("Before: {}".format(sup))
   ...:         sup.__init__()
   ...:         print("After: {}".format(sup))
   ...:         sup.__init__()
   ...:

In [3]: B()
Before: <super: <class 'B'>, NULL>
After: <super: <class 'B'>, <B object>>
finally!

Ответ 2

Путаница здесь возникает из-за того, что (в контексте определения класса) super() предоставляет связанный объект super, который затем делегирует __init__ его __self_class__, а super(B) создает несвязанный super объект, который, поскольку его __self_class__ равен None, не делегирует.

In [41]: class Test(int):
    ...:     def __init__(self):
    ...:         print(super().__self_class__)
    ...:         print(super().__init__)
    ...:         print(super(Test).__self_class__)
    ...:         print(super(Test).__init__)
    ...:

In [42]: Test()
<class '__main__.Test'>
<method-wrapper '__init__' of Test object at 0x10835c9c8>
None
<method-wrapper '__init__' of super object at 0x10835c3c8>

Поэтому, когда вы вызываете super(B).__init__(), он создает несвязанный super, а затем сразу вызывает на нем __init__; что из-за магии, описанной в различных ссылках в этом другом ответе, связывается этот unbound super. Нет ссылок на него, поэтому он исчезает, но то, что происходит под капотом.