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


99

Тож я слідкував за Супер вважається Питоном Шкідливим і пішов перевірити його приклади.

Однак у прикладі 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, і він не вдається на обох.

То який правильний спосіб вирішити цю проблему?


2
Мій кращий спосіб: Плоскі і прості ієрархії успадкування.
мільйозу

19
Ви також повинні прочитати "Супер () вважається супер", щоб отримати збалансований вигляд :)
Бьорн Поллекс

@ BjörnPollex: Дякую! Ваше посилання дає відповідь на питання: Ви можете написати «кореневої клас» , який успадковує від object, і це гарантує , що на заклик object«S __init__правильно.
cha0site

4
Зауважте, що __init__при objectбеззвучному ігноруванні будь-яких параметрів на Python 2.5. Це змінилося в Python 2.6.
Вільфред Х'юз

1
@Wilfred: Ей, дякую за відповідь на власне питання! Тепер я знаю, чому твір застарів!
cha0site

Відповіді:


103

Іноді два класи можуть мати спільні деякі назви параметрів. У такому випадку ви не можете вимкнути пари ключових значень **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потрібно мати передостанній клас у МРО.


4
Не варто Baseдзвонити super(Base, self).__init__()?
cha0site

4
Щоб це працювало, Baseнеобхідно прийти наприкінці MRO (крім object). Дзвінок object.__init__нічого не робить, тому добре не дзвонити super(Base, self).__init__(). Насправді, я думаю, що може бути зрозумілішим не включати, super(Base, self).__init__()щоб загнати додому точку, яка Baseє кінцем рядка.
unutbu

25

Якщо у вас буде багато спадщини (ось так), я пропоную вам передати всі параметри, використовуючи **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)

6

Як пояснено в супер () Python, який вважається супер , одним із способів є клас з'їсти аргументи, які йому потрібні, і передати решту далі. Таким чином, коли ланцюг викликів досягне 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)

Отже, що ж E(arg = 10)? Мені цей метод не подобається, мені потрібно знати МРО заздалегідь, щоб ним скористатися. Інший метод, коли я пишу "клас коренів", який успадковується, objectздається набагато чистішим.
cha0site

Цей метод корисний, якщо викликані функції не поділяють імена аргументів. Я визнаю, що у мене немає великого практичного досвіду super, тому такий підхід справді може бути досить теоретичним.
Бьорн Поллекс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.