当我打电话给'super(some_cls)时,有没有魔法?

时间:2018-01-17 14:50:40

标签: python python-3.x super

在调查this question时,我遇到了单一参数super的奇怪行为:

调用super(some_class).__init__()some_class(或其子类)的方法内部工作,但在其他地方调用时会抛出异常。

代码示例:

class A():                                                                                         
    def __init__(self):                                                         
        super(A).__init__()  # doesn't throw exception

a = A()
super(A).__init__()  # throws exception

抛出的异常是

Traceback (most recent call last):
  File "untitled.py", line 8, in <module>
    super(A).__init__() # throws exception
RuntimeError: super(): no arguments

我不明白为什么电话会议的位置有所不同。

众所周知,super performs magic的零参数形式:

  

零参数形式仅适用于类定义,因为编译器填写必要的细节以正确检索正在定义的类,以及访问普通方法的当前实例。

但是,super的单参数形式不存在这样的陈述。相反:

  

另请注意,除零参数形式外,super()不限于使用内部方法。

所以,我的问题是,幕后究竟发生了什么?这是预期的行为吗?

2 个答案:

答案 0 :(得分:3)

在这两种情况下,都会提供一个未绑定的超级对象。当你打电话给super(A)时,它被调用而没有参数。在没有参数的情况下调用__init__()时,编译器会尝试推断出参数:(来自typeobject.c第7434行,最新来源)

super.__init__

几行之后:(同上,第7465行)

static int
super_init(PyObject *self, PyObject *args, PyObject *kwds)
{
    superobject *su = (superobject *)self;
    PyTypeObject *type = NULL;
    PyObject *obj = NULL;
    PyTypeObject *obj_type = NULL;

    if (!_PyArg_NoKeywords("super", kwds))
        return -1;
    if (!PyArg_ParseTuple(args, "|O!O:super", &PyType_Type, &type, &obj))
        return -1;

    if (type == NULL) {
        /* Call super(), without args -- fill in from __class__
           and first local variable on the stack. */

当您致电 f = PyThreadState_GET()->frame; ... co = f->f_code; ... if (co->co_argcount == 0) { PyErr_SetString(PyExc_RuntimeError, "super(): no arguments"); return -1; } 时,会绕过此推断行为,因为类型不是无。当你在未绑定的超级上调用super(A)时 - 因为它没有被绑定,这个__init__()调用没有被代理 - 类型参数无和编译器试图推断。在类定义中,self参数存在并用于此目的。在外面,没有可用的参数,因此引发了异常。

换句话说,__init__ 的行为方式不同,具体取决于调用的位置 - 它的super(A)行为不同,而且&{ #39;正是文档建议的内容。

答案 1 :(得分:1)

(注意 - 我已经基本上编辑了这个答案,与实际问题更相关。)

如果你打电话给sup = super(A); sup.__init__(),结果与你调用sup = super()完全一样:在一个类定义中,你得到一个超级绑定;在外面,你得到一个RuntimeError(其原因在于我的另一个答案)。

这是一个确证的片段:

In [1]: class A:
   ...:     def __init__(self):
   ...:         print("finally!")
   ...:
   ...:
   ...: class B(A):
   ...:     def __init__(self):
   ...:         noarg = super()
   ...:         print("No-arg super: {}".format(noarg))
   ...:         onearg = super(B) # creates unbound super
   ...:         print("One-arg before: {}".format(onearg))
   ...:         onearg.__init__() # initializes sup as if sup = super()
   ...:         print("One-arg after: {}".format(onearg))
   ...:         onearg.__init__() # calls B.__init__()
   ...:

In [2]: B()
No-arg super: <super: <class 'B'>, <B object>>
One-arg before: <super: <class 'B'>, NULL>
One-arg after: <super: <class 'B'>, <B object>>
finally!