关于oop:python超级调用子方法

python super calling child methods

关于super()的用法有很多问题,但似乎没有一个能回答我的问题。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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()

但失败的是

1
2
3
4
5
6
7
8
9
10
11
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
2
3
4
5
6
7
8
9
10
11
12
13
14
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通过添加类名(加上下划线)来"改变"这种方法名,以最小化子类用自己的版本覆盖它们的机会。

《Pep8 Python风格指南》对私有名称管理有这样的描述:

If your class is intended to be subclassed, and you have attributes
that you do not want subclasses to use, consider naming them with
double leading underscores and no trailing underscores. This invokes
Python's name mangling algorithm, where the name of the class is
mangled into the attribute name. This helps avoid attribute name
collisions should subclasses inadvertently contain attributes with the
same name.

Note 1: Note that only the simple class name is used in the mangled
name, so if a subclass chooses both the same class name and attribute
name, you can still get name collisions.

Note 2: Name mangling can make certain uses, such as debugging and
__getattr__(), less convenient. However the name mangling algorithm
is well documented and easy to perform manually.

Note 3: Not everyone likes name mangling. Try to balance the need to
avoid accidental name clashes with potential use by advanced callers.


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


如果我们通过B的安装步骤:

  • 我们叫super(B,self).__init__,也就是说,A.__init__
  • 我们称之为self.a(),也就是说,B.a()
  • 使用self.bnum

除了bnum还没有定义…所以,AttributeError

在这种情况下,在调用super(B,self).__init__之前,只需在B.__init__中定义你的bnum

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

在进入名称混乱的黑暗路径之前,您可能想花些时间组织子类的代码:应该在父类之前执行,初始化一些特殊变量,还是在父类之后执行,以替换一些默认值?


考虑这个电话:

1
2
3
class B(A):
    def __init__(self):
        A.__init__(self)

当你叫super().__init__()时就会发生这种情况。这又称为self.a(),当然是B类的A函数,而不是A函数,因为self属于B类。如Martijn所述,可以使用双下划线名称,也可以显式使用类名,但否则无法从超类调用重写方法。