Super 可以处理多重继承吗?

12 投票
2 回答
4022 浏览
提问于 2025-04-17 05:02

当从两个对象继承时,比如这样

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)

那这个 super 是怎么知道我想同时调用两个的呢?如果是的话,它又怎么知道该把哪个参数传给哪个呢?或者说在这种情况下根本就不能使用 super?

即使 Foo 和 Bar 接受相同的参数,super 能处理这种情况吗?

还是说我根本就不应该尝试这样做?

2 个回答

2

我是不是根本就不应该尝试做这种事情呢?

没错。你应该只调用一个父类的 __init__ 方法,而不是所有的父类。super 会根据方法解析顺序(MRO)来进行深度优先搜索,找到合适的 __init__ 方法来调用。它不会也不应该调用所有可能的 __init__ 方法。

8

使用 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__ 本身是个空操作)。这样你就可以 确保 这个方法总是最后被调用,你可以随意处理你的参数。

撰写回答