python超级调用子方法

时间:2012-09-18 13:38:19

标签: python oop superclass

关于super()的使用有很多问题,但它们似乎都没有回答我的问题。

从子类调用super().__init__()时,超级构造函数中的所有方法调用实际上都来自子类。考虑以下类结构:

class A(object):
    def __init__(self):
        print("initializing A")
        self.a()
    def a(self):
        print("A.a()")

class B(A):
    def __init__(self):
        super().__init__()
        # add stuff for B
        self.bnum=3 # required by B.a()        
    def a(self):
        print("B.a(), bnum=%i"%self.bnum)

b=B()

失败
initializing A
Traceback (most recent call last):
  File "classmagic.py", line 17, in 
    b=B()
  File "classmagic.py", line 11, in __init__
    super().__init__()
  File "classmagic.py", line 5, in __init__
    self.a()
  File "classmagic.py", line 15, in a
    print("B.a(), bnum=%i"%self.bnum)
AttributeError: 'B' object has no attribute 'bnum'

这里我调用B()中的超级构造函数来初始化一些基本结构(其中一些作为自己的函数a()执行)。但是,如果我也覆盖a()函数,则在调用A的构造函数时会使用此实现,因为AB一无所知并且可能使用不同的内部A变量

这可能是也可能不直观,但当我希望{{1}}中的所有方法只能访问在那里实现的功能时,我该怎么做?

4 个答案:

答案 0 :(得分:5)

如果您的代码来调用无法覆盖的特定私有方法,请使用以两个下划线开头的名称:

class A(object):
    def __init__(self):
        print("initializing A")
        self.__a()
    def __a(self):
        print("A.a()")

class B(A):
    def __init__(self):
        super().__init__()
        # add stuff for B
        self.bnum=3 # required by B.a()        
    def __a(self):
        print("B.__a(), bnum=%i"%self.bnum)

Python "mangles"这样的方法名称通过添加类名(加上下划线)来最小化子类用自己的版本覆盖它们的机会。

PEP 8 Python Style Guide有关于私人名称修改的说法:

  

如果您的类要进行子类化,并且您具有属性   您不希望子类使用,请考虑使用它们命名   双引导下划线和没有尾随下划线。这会调用   Python的名称修改算法,其中类的名称是   被破坏成属性名称。这有助于避免属性名称   碰撞应该是子类无意中包含的属性   同名。

     

注1:请注意,在错位中只使用简单的类名   name,所以如果子类选择相同的类名和属性   名字,你仍然可以得到名字冲突。

     

注2:名称修改可以进行某些用途,例如调试和   __getattr__(),不太方便。但是名称为mangling算法   有详细记录,易于手动执行。

     

注3:不是每个人都喜欢名字错误。尝试平衡需要   避免意外姓名冲突,可能会被高级来电者使用。

答案 1 :(得分:2)

您需要使用self.a(),而不是致电A.a(self)。但是对于特定类的所有方法并不常见,您应该重新评估B是否应该继承A

答案 2 :(得分:0)

考虑这个电话:

class B(A):
    def __init__(self):
        A.__init__(self)

当您致电super().__init__()时会发生这种情况。反过来,这会调用self.a(),这当然是a类的函数B,而不是A,因为self属于B类}。正如Martijn所说,你可以使用双下划线名称,或明确使用类名,但是否则不可能从超类中调用重写方法。

答案 3 :(得分:0)

如果我们完成B的实例化步骤:

  • 我们致电super(B,self).__init__,即A.__init__
  • 在我们的案例中调用self.a(),即B.a()
  • 使用self.bnum

除了bnum尚未定义...所以,AttributeError

对于这种特殊情况,只需在bnum 之前定义B.__init__ ,然后再调用super(B,self).__init__

class B(A):
    def __init__(self):
       self.bnum=3
       super(B, self).__init__()

在走向黑暗,黑暗的名称修剪路径之前,您可能只想花时间组织子类的代码:如果在之前执行,则初始化一些特殊变量或之后的,以替换一些默认值?