运行子方法而不是父方法?

时间:2013-07-03 15:38:36

标签: python class inheritance

我有以下类继承架构:

object <- A <- B <- Z

现在每个班级都有公共setup()方法,该方法会调用其他“私有”方法_configure(),其中包含我不想混淆的加载代码setup()。重点是让每个班级“知道”如何设置自己,并在其他执行之前完成。

所以在Z的实例上调用setup时我想要的是运行两个 A的设置和B的设置(现在顺序不是特别重要),而每个设置使用_configure,如其自己的类中所定义。

现在关注脚本

#!/usr/bin/python

class A(object):

    def __init__(self):
        self.configured = []
        self.set_up = []

    def _configure(self):
        self.configured.append("A")

    def setup(self):
        self._configure()       # calls B._configure()!
        self.set_up.append("A")

class B(A):

    def _configure(self):
        self.configured.append("B")

    def setup(self):
        super(B, self).setup()
        self._configure()
        self.set_up.append("B")

class Z(B):
    pass

if __name__ == "__main__":
    z = Z()
    z.setup()
    print "configured: %s" % z.configured
    print "set up: %s" % z.set_up

运行B._configure()两次,因此返回

me@here:~$ ./t.py 
configured: ['B', 'B']
set up: ['A', 'B']
me@here:~$

而不是configured: ['A', 'B']...

有人可以向我解释一下吗?我应该如何确保A.setup来电A._configure

解决方法:现在有用的是用self._configure替换A._configure(self),但这看起来很丑陋而且非OOP:现在每个可能被继承的类都应该重复它每个方法调用的名称? self的美丽和简洁在哪里?

2 个答案:

答案 0 :(得分:3)

班级setup中的B方法调用super,因此它从setup调用A方法...这是执行的顺序你的方法:

Z.setup()
  B.setup()
    A.setup (via super)
      B._configure
        B.configured.append("B")
      B.setup.append("A")
    B._configure
      B.configured.append("B")
  B.set_up.append("B")

这意味着Z.setup通过继承调用B.setup,后者通过调用A.setup的{​​{1}}调用superB._configure再次调用B.setup并在B._configure之后返回。

希望这表明(或多或少)

更新:以下是对正在发生的事情的一点解释:您正在定义一个类append,其中包含公共方法Z和私有方法setup。这些方法未在_configure类中定义,而是在其父类Z上定义。但是,由于继承,您仍然可以致电BZ.setup

现在,Z._configure方法使用Z.setup类中的定义才能工作。这会调用B以便在super时调用A.setup中定义的代码,因此当调用方法Z时,将使用哪个定义?正如我们_configure(即Zself的实例),将被调用的方法将是Z,因为这是继承所做的。

再次查看代码:您将Z._configure首先在B中定义的_configure方法覆盖。由于A中没有对super的调用,如果对象是继承自B._configure的类的实例,则永远不会调用A._configure

TL; DR :如果A定义A._configure而未调用超级,则无法从Z致电B。这是继承。如果您想致电_configure,请不要覆盖A.configure中的方法或使用B

希望这有帮助! :)

答案 1 :(得分:1)

最后我明白了,必要的魔法称为name manglingname scrambling

解决方案就像在配置方法的名称中使用两个下划线一样简单。当Python看到这样有趣的命名方法(或其他属性)时,它将以静默方式将重命名为_A__configure,这样即使用户(意外或非意外)覆盖了它,也会调用正确的方法。 / p>

(到现在为止,我认为“两个下划线”是为Python内部保留的,谦卑地一直避免它们......)

固定代码:

#!/usr/bin/python

class A(object):

    def __init__(self):
        self.configured = []
        self.set_up = []

    def __configure(self):
        self.configured.append("A")

    def setup(self):
        self.__configure()
        self.set_up.append("A")

class B(A):

    def __configure(self):
        self.configured.append("B")

    def setup(self):
        super(B, self).setup()
        self.__configure()
        self.set_up.append("B")

class Z(B):
    pass

if __name__ == "__main__":
    z = Z()
    z.setup()
    print "configured: %s" % z.configured
    print "set up: %s" % z.set_up

返回所需的

me@here:~$ ./t.py 
configured: ['A', 'B']
set up: ['A', 'B']
me@here:~$