Может ли 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.