如何在Python中重写父类的函数?

12 投票
3 回答
17047 浏览
提问于 2025-04-15 20:38

我在父类里有一个私有方法 def __pickSide(self):,我想在子类中重写这个方法。但是,子类还是调用了从父类继承来的 def __pickSide(self):。我该怎么重写这个函数呢?子类的方法名字和父类的方法名字完全一样。

3 个回答

4
  • 使用 __foo 这样的命名方式会把方法的名字搞得复杂,这样在需要访问它的时候就会变得麻烦。我建议不要使用这种方式,这样测试的时候会顺利很多。

  • 在Python中没有“私有”这个概念,即使有,也不会阻止你这样做。(这就是一些有“私有”概念的编程语言的目的。)

  • 通常的约定是,用一个 前导下划线 来表示某个属性不是类的公共接口的一部分,比如 _foo。这样就足够让你的代码清晰,能够区分内部细节和公共接口。

5

你遇到的问题是,双下划线会让函数的名字变得复杂,即使在调用的时候也是如此。这会导致多态性无法正常工作,因为它变复杂后的名字是根据定义这个方法的类的名字来决定的,而不是根据你正在使用的对象的类的名字。把双下划线换成其他符号就能解决这个问题。

36

我们来看一个最简单的例子:

from dis import dis

class A(object):
  def __pick(self):
      print "1"

  def doitinA(self):
      self.__pick()

class B(A):
  def __pick(self):
      print "2"

  def doitinB(self):
      self.__pick()

b = B()
b.doitinA() # prints 1
b.doitinB() # prints 2

dis(A.doitinA)
print
dis(B.doitinB)

反汇编的结果如下:

  8           0 LOAD_FAST                0 (self)
              3 LOAD_ATTR                0 (_A__pick)
              6 CALL_FUNCTION            0
              9 POP_TOP
             10 LOAD_CONST               0 (None)
             13 RETURN_VALUE

 15           0 LOAD_FAST                0 (self)
              3 LOAD_ATTR                0 (_B__pick)
              6 CALL_FUNCTION            0
              9 POP_TOP
             10 LOAD_CONST               0 (None)
             13 RETURN_VALUE

你可以看到,Python 会把以两个下划线开头的函数名(还有对这些名字的访问)改成一个包含类名的名字——在这个例子中是 _A__pick_B__pick。这意味着,函数定义在哪个类里,就决定了调用哪个 __pick 方法。

解决这个问题的方法很简单,就是避免使用伪私有的方法,直接去掉两个下划线。比如,可以用 _pick 代替 __pick

撰写回答