关于子类:在python中调用超类的__init__时显式传递Self

Explicit passing of Self when calling super class's __init__ in python

这个问题与"super"在python中的作用有关?,如何初始化基本(超级)类?和python:如何从超类创建子类?它描述了从SubClass中初始化SuperClass的两种方法,即

1
2
3
4
5
6
7
8
9
10
11
12
13
class SuperClass:
    def __init__(self):
        return
    def superMethod(self):
        return


## One version of Initiation
class SubClass(SuperClass):
    def __init__(self):
        SuperClass.__init__(self)
    def subMethod(self):
        return

1
2
3
4
5
6
7
8
9
10
11
12
class SuperClass:
    def __init__(self):
        return
    def superMethod(self):
        return

## Another version of Initiation
class SubClass(SuperClass):
    def __init__(self):
        super(SubClass, self).__init__()
    def subMethod(self):
        return

所以我对需要显式地将self作为参数传递给埃多克斯1〔3〕和super(SubClass, self).__init__()。(事实上,如果我打电话给SuperClass.__init__(),我就得到了错误。

1
TypeError: __init__() missing 1 required positional argument: 'self'

)但当调用构造函数或任何其他类方法(即:

1
2
3
4
5
6
7
8
## Calling class constructor / initiation
c = SuperClass()
k = SubClass()

## Calling class methods
c.superMethod()
k.superMethod()
k.subMethod()

)隐式传递self参数。

我对EDCOX1的2个关键字的理解是,它与C++中的EDCOX1×8指针无关,而它提供了对类实例的引用。这是正确的吗?

如果总是有一个当前实例(在本例中是SubClass),那么为什么需要在对SuperClass.__init__(self)的调用中显式地包括self

谢谢


这只是简单的方法绑定,与super几乎没有关系。当可以使用x.method(*args)时,python会检查x的类型,以查找名为method的方法。如果找到一个,它会将函数"绑定"到x,这样当您调用它时,x将作为第一个参数传递,在其余参数之前。

当您通过类调用(普通)方法时,不会发生这种绑定。如果方法期望其第一个参数是一个实例(例如self),则需要自己传递它。

这种绑定行为的实际实现非常整洁。如果python对象有一个__get__方法(和/或__set____delete__方法,那么它们就是"描述符",但这些方法并不重要。当您查找像a.b这样的属性时,python会检查a的类,看它是否有一个属性b是一个描述符。如果是这样,它会把a.b翻译成type(a).b.__get__(a, type(a))。如果b是一个函数,它将有一个__get__方法来实现我上面描述的绑定行为。其他类型的描述符可以有不同的行为。例如,classmethod修饰符用一个特殊的描述符替换了一个方法,该描述符绑定了函数类,而不是实例。

python的super创建了特殊的对象,这些对象处理属性查找的方式与普通对象不同,但对于这个问题来说,细节并不重要。通过super调用的方法的绑定行为与我在第一段中描述的一样,因此当调用时,self会自动传递给绑定方法。super的唯一特殊之处在于,它绑定的函数可能与在self上查找相同方法名的函数不同(这就是使用它的全部意义)。


下面的例子可能会说明一些事情:

1
2
3
4
5
6
7
8
9
class Example:
    def method(self):
        pass

>>> print(Example.method)
<unbound method Example.method>

>>> print(Example().method)
<bound method Example.method of <__main__.Example instance at 0x01EDCDF0>>

绑定方法时,将隐式传递实例。当方法未绑定时,需要显式传递实例。

其他答案肯定会提供关于绑定过程的更多细节,但我认为值得展示上面的代码片段。


答案是不平凡的,可能会保证一篇好文章。Raymond Hettinger在Pycon 2015年的演讲中对super()的工作原理给出了一个非常好的解释,该演讲在这里和相关文章中都有介绍。我将尝试一个简短的答案,如果它不够,我(希望社区)将扩大它。答案有两个关键部分:

  • python的super()需要有一个对象,在该对象上调用被重写的方法,因此它是用self显式传递的。这不是唯一可能的实现,事实上,在Python3中,不再需要您传递自实例。

  • Python EDCOX1 9不象Java,或其他编译语言EDCOX1,12。正如Hettinger的谈话中所解释的,Python的实现旨在支持多协作继承范式。这在python中有一个有趣的结果:super()中的方法解析不仅依赖于父类,还依赖于子类(多重继承的结果)。注意,Hettinger使用的是python 3。

  • 小精灵

    关于super的官方python 2.7文档也是一个很好的信息来源(在我看来,在观看了谈话之后,更好地理解了这一点)。


    因为在SuperClass.__init__(self)中,您是在类上调用方法,而不是在实例上调用方法,所以不能隐式传递该方法。同样,你不能只叫SubClass.subMethod(),但你可以叫SubClass.subMethod(k),它相当于k.subMethod()。同样,如果self指的是SubClass,那么self.__init__()指的是SubClass.__init__(self),所以如果你想叫SuperClass.__init,就必须直接叫它。