2012-01-23 4 views
52

Итак, я следил за Python's Super Considered Harmful и отправился проверять его примеры.правильный способ использования супер (аргумент прохождение)

Однако Example 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

Мой предпочтительный способ: Плоские и простые иерархии наследования. – millimoose

+14

Вы также должны прочитать ["Super() рассмотренный супер"] (http://rhettinger.wordpress.com/2011/05/26/super-considered-super/), чтобы получить сбалансированный вид :) –

+0

@ BjörnPollex: Благодаря! Ваша ссылка дает ответ на вопрос: вы можете написать «корневой класс», который наследуется от 'object', и он обязательно правильно называет' object' '__init__'. – cha0site

ответ

55

Иногда два класса могут иметь некоторые имена параметров. В этом случае вы не можете выставлять пары ключ-значение из **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

Не нужно 'Base' call' super (Base, self) .__ init __() '? – cha0site

+2

Чтобы это сработало, 'Base' должен появиться в конце MRO (кроме' object'). Вызов 'object .__ init__' ничего не делает, поэтому нормально не называть' super (Base, self) .__ init __() '. На самом деле, я думаю, что может быть яснее не включать 'super (Base, self) .__ init __()', чтобы привести домой точку, в которой 'Base' является концом строки. – unutbu

5

Как объяснено в Python's super() considered 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) 
+0

Итак, что относительно 'E (arg = 10)'? Мне не нравится этот метод, мне нужно знать MRO заранее, чтобы использовать его. Другой метод, когда я пишу «корневой класс», который наследует «объект», кажется намного чище. – cha0site

+0

Этот метод полезен, если вызываемые функции не разделяют имена аргументов. Я признаю, что у меня нет большого практического опыта с «супер», поэтому этот подход действительно может быть довольно теоретическим. –

14

Если вы собираетесь иметь много наследования (что в данном случае) я предлагаю вам пройти все параметры с помощью **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) 
Смежные вопросы