首页  >  问答  >  正文

关于python的继承的一个疑问

如图所示,B 类继承了 A 类;

当实例化对象时,B 类中没有构造函数, 应该调用父类的构造函数 __init__

但是里边的 self.__pirv() 为啥调用到父类 A__priv, 而 self.pub() 又调到 B 中的 pub

求解?

迷茫迷茫2741 天前864

全部回复(3)我来回复

  • ringa_lee

    ringa_lee2017-04-18 10:30:50

    在Python从__开始的方法名称不是private,__的意思就让Python做name mangling,name mangling的结果就是_A__priv。这样的方法你应该不要overwrite。如果你想让子类overwrite一个方法你只用_,不用__

    回复
    0
  • 大家讲道理

    大家讲道理2017-04-18 10:30:50

    想了一下这个问题,我是这样理解的:

    class A(object):
        def __init__(self):
            self.__priv()  # _A__priv()
            self.pub()
    
        def __priv(self):
            print("private A")
    
        def pub(self):
            print("public A")
    
    
    class B(A):
        def __init__(self):
            self.__priv()    # 调用_B__priv()对比
            super(B, self).__init__()  # 在这里显式调用父类的`__init__()`方法
    
        def __priv(self):
            print("private B")
    
        def pub(self):
            print("public B")
    
    
    if __name__ == '__main__':
        b = B()

    在子类的实例调用__init__()方法时,从子类B本身中查找pub()方法,显然是存在的,因此会调用B类自身的pub()方法;然而在调用__priv()方法时,由于Python对私有成员进行了名称改编,你实际调用的是_A__priv()方法,而B类中并没有这个方法,有的只有_B__priv(),因此调用父类A中的_A__priv(),于是产生了这样的结果。这是我个人的理解,如果有误欢迎指正,谢谢。

    回复
    0
  • 巴扎黑

    巴扎黑2017-04-18 10:30:50

    关于问题本身, @Xavier 和 @Christoph 已经有详细的说明了


    如果你还不明白可以试着这样做:

    原本的代码:

    class A:
        def __init__(self):
            self.__priv() # 等等改成 self._A__priv()
            self.public()
        def __priv(self): # 等等改成 def _A__priv(self):
            print('private of A')
        def public(self):
            print('public of A')
            
    class B(A):
        def __priv(self): # 等等改成 self._B__priv(self):
            print('private of B')
        def public(self):
            print('public of B')
            
    b = B()

    自行手动进行 name mangling:

    class A:
        def __init__(self):
            self._A__priv()
            self.public()
        def _A__priv(self):
            print('private of A')
        def public(self):
            print('public of A')
            
    class B(A):
        def _B__priv(self):
            print('private of B')
        def public(self):
            print('public of B')
            
    b = B()

    B 在這裡繼承了 A 的所有属性包含:

    • __init__

    • _A__priv

    • public

    B 自己定义了:

    • _B__priv

    • public (此處覆寫了 Apublic (此处覆写了 A

      )

    dir(b)所以最后你会看到

    里面有:
    • __init__ (從 A

      (从 A 继承的)
    • _A__priv (從 A

      (从 A 继承的)
    • _B__priv

      (自己定义的)
    • public

      (自己定义的)

    __init__ 被呼叫時, 會調用 _A__priv, 而 B最后当

    被呼叫时, 会调用
    , 而

    里面的确有这个方法

    啰唆补充一下, Python 本身并没有真正的 private 机制, 因为了解 name mangling 的人就能对以双底线开头的属性做存取, 比如说我可以很轻易地写出:
      a = A()
      a._A__priv() # 防都防不住
    • 简单来说这个机制是个:

      防呆

      的机制, 不是个
    • 防小人
    • 的机制,

      防止意外

      存取的机制, 不是个防止
    • 刻意存取
    的机制

    但是这个机制并非所有人都觉得好(个人就不喜欢, 使用双底线开头命名既麻烦也没太多实际的帮助), 所以你可以在很多的python 代码中发现: 大家比较常使用以

    单个底线

    开头的保护方式, 这种做法是个公认的惯例(注1), 对于稍有经验的程序员来说足以防呆, 且不会有任何额外的效果和意外的状况发生

    Ian Bicking 有一段话是这样说的 (Ian Bicking 是 Python 大神, 这段话我是在 Luciano Ramalho 的的 Fluent Python 中看到的):

    永远不要在前面使用两个底线. 这是很让人生气的自私行为, 如果你不希望造成名称冲突(注2), 可以明确地重整名称(例如: _MyThing_blahblah). 实质上这与使用双底线是同一件事情, 不过他是公开的, 双底线是私下的行为._priv


    所以我的建议是, 使用

    会是更好的选择.const 注1: 以单底线开头的属性不会具有任何特殊的性质, 他仅仅是依靠Python 程序员的共识而产生的具有象征意义的符号性手法, 就好像有些语言会使用

    来标明常量, 而我们也可以仅依赖

    常量使用大写命名

    的共识来避免意外的状况发生🎜 🎜注2: 之所以想要以 private 性质来保护属性, 最常见的就是因为名称冲突引起的意外存取🎜

    我回答过的问题: Python-QA

    回复
    0
  • 取消回复