Python - 涉及多重继承时调用祖先方法

时间:2012-09-05 02:15:28

标签: python inheritance multiple-inheritance

编辑:我正在使用Python 3(有些人问过)。

我认为这只是一个语法问题,但我想确定没有什么是我遗漏的。请注意Foo和Bar的实现方式的语法差异。他们实现了同样的目标,我想确保他们真的做同样的事情。输出表明只有两种方法可以做同样的事情。是这样的吗?

代码:

class X:

    def some_method(self):
        print("X.some_method called")

class Y:

    def some_method(self):
        print("Y.some_method called")

class Foo(X,Y):

    def some_method(self):
        X().some_method()
        Y().some_method()
        print("Foo.some_method called")

class Bar(X,Y):

    def some_method(self):
        X.some_method(self)
        Y.some_method(self)
        print("Bar.some_method called")

print("=== Fun with Foo ===")
foo_instance = Foo()
foo_instance.some_method()

print("=== Fun with Bar ===")
bar_instance = Bar()
bar_instance.some_method()

输出:

=== Fun with Foo ===
X.some_method called
Y.some_method called
Foo.some_method called
=== Fun with Bar ===
X.some_method called
Y.some_method called
Bar.some_method called
PS - 希望不言而喻,但这只是一个抽象的例子,让我们不要担心为什么我想在两个祖先上调用some_method,我我只是想在这里理解语言的语法和机制。谢谢大家!

2 个答案:

答案 0 :(得分:3)

  1. 您应该使用新式课程。如果这是Python 3,那么你就是;如果你使用的是Python 2,你应该继承object(或其他一些新式的类)。
  2. 调用祖先方法的常用方法是使用super。在标准文档中阅读它,以及有关它如何运作的其他优秀文章。永远不建议以您的方式调用方法,因为(a)在进一步继承时它将是脆弱的; (b)通过硬编码对类的引用来增加维护工作量。
  3. 更新:以下是展示如何使用super来实现此目的的示例:http://ideone.com/u3si2

    另请参阅:http://rhettinger.wordpress.com/2011/05/26/super-considered-super/

    更新2:这是python 2的一个小库,它为每个方法添加__class__变量和无参数super,以避免对当前名称进行硬编码:https://github.com/marcintustin/superfixer

答案 1 :(得分:2)

他们不一样。 X()创建了一个类X的对象。执行X().someMethod()时,您创建一个新对象,然后在该对象上调用该方法,而不是selfX.someMethod(self)是你想要的,因为它在同一个对象上调用了继承的方法。

如果您的方法实际对self对象执行任何操作,您将看到区别。例如,如果您将self.blah = 8放入方法中,那么在X.someMethod(self)之后,您调用它的对象将设置blah属性,但在X().someMethod()后它不会。 (相反,您将创建一个新对象,在其上设置blah,然后抛弃该新对象而不使用它,保持原始对象不变。)

以下是修改代码的简单示例:

>>> class X:
... 
...     def some_method(self):
...         print("X.some_method called on", self)
... 
... class Y:
... 
...     def some_method(self):
...         print("Y.some_method called on", self)
... 
... class Foo(X,Y):
... 
...     def some_method(self):
...         X().some_method()
...         Y().some_method()
...         print("Foo.some_method called on", self)
... 
... class Bar(X,Y):
... 
...     def some_method(self):
...         X.some_method(self)
...         Y.some_method(self)
...         print("Bar.some_method called on", self)
>>> Foo().some_method()
('X.some_method called on', <__main__.X instance at 0x0142F3C8>)
('Y.some_method called on', <__main__.Y instance at 0x0142F3C8>)
('Foo.some_method called on', <__main__.Foo instance at 0x0142F3A0>)
>>> Bar().some_method()
('X.some_method called on', <__main__.Bar instance at 0x0142F3C8>)
('Y.some_method called on', <__main__.Bar instance at 0x0142F3C8>)
('Bar.some_method called on', <__main__.Bar instance at 0x0142F3C8>)

请注意,当我使用Foo时,打印的对象不一样;一个是X实例,一个是Y实例,最后一个是我调用方法的原始Foo实例。使用Bar时,它在每个方法调用中都是相同的对象。

(在某些情况下,您也可以使用super来避免明确命名基类;例如,super(Foo, self).someMethod()或Python 3只是super().someMethod()。但是,如果您需要从两个基类直接调用继承的方法,super可能不太适合。它通常针对每个方法只调用super一次,将控制权传递给方法的下一个版本的情况。继承链,然后将它传递给下一个,等等。)