Итак, я следил за 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, и на обоих ничего не вышло.
Итак, как правильно решить эту проблему?
object
, и это гарантирует , что на призывobject
«S__init__
правильно.__init__
onobject
молча игнорирует любые параметры в Python 2.5. Это изменилось в Python 2.6.Ответы:
Иногда у двух классов могут быть общие имена параметров. В этом случае вы не можете
**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.источник
Base
звонитьsuper(Base, self).__init__()
?Base
необходимо указать конец MRO (кромеobject
). Звонкиobject.__init__
ничего не делают, поэтому не звонить - это нормальноsuper(Base, self).__init__()
. На самом деле, я думаю, может быть более понятным не включатьsuper(Base, self).__init__()
в рассмотрение точку, котораяBase
является концом линии.Если у вас будет много наследственности (как в данном случае), я предлагаю вам передать все параметры using
**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)
источник
Как объясняется в 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)
источник
E(arg = 10)
? Мне этот метод не нравится, мне нужно заранее знать MRO, чтобы им пользоваться. Другой метод, в котором я пишу «корневой класс», унаследованный от него,object
кажется намного чище.super
, поэтому этот подход действительно может быть скорее теоретическим.