在Python中调用超类的 __init__ 时显式传递 Self

9
这个问题与以下内容有关:Python中的'super'是什么?如何初始化基类(super class)?Python:如何从超类(superclass)创建一个子类(subclass)?。它们描述了两种在SubClass内部初始化SuperClass的方法。
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

或者

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

所以,我对需要在SuperClass.__init__(self)super(SubClass, self).__init__()显式传递self参数有些困惑。(事实上,如果我调用SuperClass.__init__(),我会得到错误提示。

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

当调用构造函数或任何其他类方法(例如:

## Calling class constructor / initiation
c = SuperClass()
k = SubClass()

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

), self参数被隐式地传递。

我对self关键字的理解是,它与C ++中的this指针类似,提供对类实例的引用。这正确吗?

如果总会有一个当前实例(在这种情况下是SubClass),那么为什么需要在调用SuperClass.__init__(self)时明确包含self

谢谢

4个回答

7
这只是简单的方法绑定,与 "super" 几乎没有什么关系。当你使用 "x.method(*args)" 时,Python 会检查类型为 "method" 的名称是否存在于 "x" 中。如果存在,则将该函数"绑定"到 "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" 上查找相同方法名的结果上(这就是使用它的全部意义)。

4
以下示例可能会有所帮助:
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>>

当一个方法被绑定时,实例会被隐式传递。当一个方法未被绑定时,实例需要显式传递。

其他答案肯定会对绑定过程提供更多细节,但我认为展示上述片段是值得的。


2
答案并不简单,可能需要一篇好的文章来解释。Raymond Hettinger在2015年的Pycon演讲中精彩地解释了super()的工作原理,可在此处观看,并有相关的文章
我会尝试给出一个简短的答案,如果不够详细,我(以及希望社区)将进一步扩展。
答案有两个关键点:
  • Python的super()需要在被重写的方法上调用一个对象,因此需要显式传递self。这不是唯一可能的实现方式,事实上,在Python 3中,不再需要传递self实例。

  • Python的super()与Java或其他编译语言不同。Python的实现旨在支持多协作继承范例,如Hettinger所解释的那样。这在Python中具有有趣的后果:super()中的方法解析不仅取决于父类,还取决于子类(多重继承的结果)。请注意,Hettinger使用的是Python 3。

官方的Python 2.7文档也是一个很好的信息来源(在我看来,在观看演讲后更易理解)。

1
因为在SuperClass.__init__(self)中,你是在类上调用方法,而不是实例,所以它不能被隐式传递。同样,你不能只调用SubClass.subMethod(),但你可以调用SubClass.subMethod(k),它将等同于k.subMethod()。同样,如果self指向一个SubClass,那么self.__init__()意味着SubClass.__init__(self),所以如果你想调用SuperClass.__init__,你必须直接调用它。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接