Может ли Super справиться с множественным наследованием?

При наследовании от двух объектов, подобных этим

class Foo(object):
  def __init__(self,a):
    self.a=a

class Bar(object):
  def __init__(self,b):
    self.b=b

Я бы обычно делал что-то подобное

class FooBar(Foo,Bar):
  def __init__(self,a,b):
    Foo.__init__(self,a)
    Bar.__init__(self,b)

Как супер узнает, хочу ли я позвонить обоим? и если да, то как он узнает, какой аргумент передать, где. Или это просто невозможно для пользователя супер здесь?

Даже если Foo и Bar используют одинаковые аргументы, super справиться с этим?

Или я не должен пытаться делать это в первую очередь?

2 ответа

Решение

С помощью super(), __init__() и множественное наследование немного сложнее.

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

>>> class Foo(object):
...     def frob(self, arg):
...         print "Foo.frob"
...         if hasattr(super(Foo, self), 'frob'):
...             super(Foo, self).frob(arg)
... 
>>> class Bar(object):
...     def frob(self, arg):
...         print "Bar.frob"
...         if hasattr(super(Bar, self), 'frob'):
...             super(Bar, self).frob(arg)
... 
>>> class Baz(Foo, Bar):
...     def frob(self, arg):
...         print "Baz.frob"
...         super(Baz, self).frob(arg)
... 
>>> b = Baz()
>>> b.frob(1)
Baz.frob
Foo.frob
Bar.frob
>>> 

Но когда вы пытаетесь сделать что-то похожее на метод, который object на самом деле, вещи становятся немного рискованными; object.__init__ не принимает аргументы, поэтому о единственном безопасном способе его использования является вызов super().__init__() без аргументов, так как этот вызов может быть обработан object.__init__, Но тогда это не может быть обработано object.__init__и вместо этого обрабатывается классом в другом месте в графе наследования. Таким образом, любой класс, который определяет __init__ в классе множественного наследования heirarchy должен быть подготовлен для вызова без аргументов.

один из способов справиться с этим - никогда не использовать аргументы в __init__(), Выполните минимальную инициализацию и полагайтесь на настройку свойств или использование других средств для настройки нового объекта перед использованием. Это довольно неприятно, хотя.

Другой способ - использовать только ключевые аргументы, что-то вроде def __init__(self, **keywords): и всегда удаляйте аргументы, которые применяются к данному конструктору. Это основанная на надежде стратегия, вы надеетесь, что все ключевые слова будут использованы до того, как контроль достигнет object.__init__,

Третий способ - определить суперкласс для всех множественных наследуемых баз, которые сами определяют __init__ каким-то полезным способом и не вызывает super().__init__ (object.__init__ в любом случае не работает). Это означает, что вы можете быть уверены, что этот метод всегда вызывается последним, и вы можете делать все, что захотите, с вашими аргументами.

Или я не должен пытаться делать это в первую очередь?

Правильный. Вы должны звонить только одной родительской линии __init__с, а не дерево. super будет использовать MRO для поиска родительских классов в глубину в поисках правильных __init__ звонить. Это не будет и не должно называть все возможное __init__s.

Другие вопросы по тегам