Может ли Super справиться с множественным наследованием? - PullRequest
10 голосов
/ 26 октября 2011

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

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 ]

7 голосов
/ 26 октября 2011

Использование 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__ в иерархии множественного наследования, должен быть подготовлен для вызова с без аргументов .

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

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

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

2 голосов
/ 26 октября 2011

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...