Правильный способ использования супер (аргумент передачи)

Итак, я следил за Python Super считался вредным и отправился проверять свои примеры.

Однако Пример 1-3, который должен показывать правильный способ вызова super при обработке __init__ методов, которые ожидают разные аргументы, выравнивание не работает.

Это то, что я получаю:

~ $ python example1-3.py 
MRO: ['E', 'C', 'A', 'D', 'B', 'object']
E arg= 10
C arg= 10
A
D arg= 10
B
Traceback (most recent call last):
  File "Download/example1-3.py", line 27, in <module>
    E(arg=10)
  File "Download/example1-3.py", line 24, in __init__
    super(E, self).__init__(arg, *args, **kwargs)
  File "Download/example1-3.py", line 14, in __init__
    super(C, self).__init__(arg, *args, **kwargs)
  File "Download/example1-3.py", line 4, in __init__
    super(A, self).__init__(*args, **kwargs)
  File "Download/example1-3.py", line 19, in __init__
    super(D, self).__init__(arg, *args, **kwargs)
  File "Download/example1-3.py", line 9, in __init__
    super(B, self).__init__(*args, **kwargs)
TypeError: object.__init__() takes no parameters

Похоже, что object сам нарушает одну из лучших практик, упомянутых в документе, то есть методы, которые используют super, должны принимать *args и **kwargs.

Теперь, очевидно, г-н Найт ожидал, что его примеры будут работать, так это то, что было изменено в последних версиях Python? Я проверил 2.6 и 2.7, и он не работает на обоих.

Итак, каков правильный способ решения этой проблемы?

Ответ 1

Иногда два класса могут иметь общие имена параметров. В этом случае вы не можете выталкивать пары ключ-значение из **kwargs или удалять их из *args. Вместо этого вы можете определить класс Base, который в отличие от object, абсорбирует/игнорирует аргументы:

class Base(object):
    def __init__(self, *args, **kwargs): pass

class A(Base):
    def __init__(self, *args, **kwargs):
        print "A"
        super(A, self).__init__(*args, **kwargs)

class B(Base):
    def __init__(self, *args, **kwargs):
        print "B"
        super(B, self).__init__(*args, **kwargs)

class C(A):
    def __init__(self, arg, *args, **kwargs):
        print "C","arg=",arg
        super(C, self).__init__(arg, *args, **kwargs)

class D(B):
    def __init__(self, arg, *args, **kwargs):
        print "D", "arg=",arg
        super(D, self).__init__(arg, *args, **kwargs)

class E(C,D):
    def __init__(self, arg, *args, **kwargs):
        print "E", "arg=",arg
        super(E, self).__init__(arg, *args, **kwargs)

print "MRO:", [x.__name__ for x in E.__mro__]
E(10)

дает

MRO: ['E', 'C', 'A', 'D', 'B', 'Base', 'object']
E arg= 10
C arg= 10
A
D arg= 10
B

Обратите внимание, что для этого, Base должен быть предпоследним классом в MRO.

Ответ 2

Если у вас будет много наследования (в данном случае здесь), я предлагаю вам передать все параметры с помощью **kwargs, а затем pop их сразу после их использования (если только они вам не нужны в верхнем классы).

class First(object):
    def __init__(self, *args, **kwargs):
        self.first_arg = kwargs.pop('first_arg')
        super(First, self).__init__(*args, **kwargs)

class Second(First):
    def __init__(self, *args, **kwargs):
        self.second_arg = kwargs.pop('second_arg')
        super(Second, self).__init__(*args, **kwargs)

class Third(Second):
    def __init__(self, *args, **kwargs):
        self.third_arg = kwargs.pop('third_arg')
        super(Third, self).__init__(*args, **kwargs)

Это самый простой способ решить такие проблемы.

third = Third(first_arg=1, second_arg=2, third_arg=3)

Ответ 3

Как объяснено в Python super() считается супер, одним из способов является то, чтобы класс питался аргументами, которые ему нужны, и передавал остальное. Таким образом, когда цепочка вызовов достигает object, все аргументы были съедены, а object.__init__ будет вызываться без аргументов (как ожидается). Поэтому ваш код должен выглядеть так:

class A(object):
    def __init__(self, *args, **kwargs):
        print "A"
        super(A, self).__init__(*args, **kwargs)

class B(object):
    def __init__(self, *args, **kwargs):
        print "B"
        super(B, self).__init__(*args, **kwargs)

class C(A):
    def __init__(self, arg, *args, **kwargs):
        print "C","arg=",arg
        super(C, self).__init__(*args, **kwargs)

class D(B):
    def __init__(self, arg, *args, **kwargs):
        print "D", "arg=",arg
        super(D, self).__init__(*args, **kwargs)

class E(C,D):
    def __init__(self, arg, *args, **kwargs):
        print "E", "arg=",arg
        super(E, self).__init__(*args, **kwargs)

print "MRO:", [x.__name__ for x in E.__mro__]
E(10, 20, 30)