当我调用 `super(some_cls)` 时会发生什么神奇的事情吗?
Does any magic happen when I call `super(some_cls)`?
在调查 时,我遇到了单参数 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:
的零参数形式
The zero argument form only works inside a class definition, as the compiler fills in the necessary details to correctly retrieve the class being defined, as well as accessing the current instance for ordinary methods.
然而,super
的单参数形式不存在这样的语句。反之:
Also note that, aside from the zero argument form, super() is not limited to use inside methods.
那么,我的问题是,幕后到底发生了什么?这是预期的行为吗?
(注意 - 我已经对这个答案进行了大量编辑,使其与实际问题更相关。)
如果调用 sup = super(A); sup.__init__()
,结果与调用 sup = super()
完全相同:在 class 定义中,您得到一个绑定的超级;在外面,你会得到一个 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!
在这两种情况下,super(A)
给出了一个未绑定的超级对象。当你在上面调用 __init__()
时,它是在没有参数的情况下调用的。当不带参数调用 super.__init__
时,编译器会尝试推断参数:(来自 typeobject.c 行 7434,最新来源)
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. */
几行之后:(同上,第 7465 行)
f = PyThreadState_GET()->frame;
...
co = f->f_code;
...
if (co->co_argcount == 0) {
PyErr_SetString(PyExc_RuntimeError,
"super(): no arguments");
return -1;
}
当您调用 super(A)
时,会绕过此推断行为,因为类型不是 None。然后当您在未绑定的 super 上调用 __init__()
- 因为它未绑定,所以此 __init__
调用未被代理 - 类型参数 是 None 并且编译器尝试推断。在 class 定义中,存在 self 参数并用于此目的。在外面,没有可用的参数,所以抛出异常。
换句话说,super(A)
而不是 根据调用位置的不同表现不同 - super.__init__()
表现不同,而这正是文档建议。
在调查 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:
The zero argument form only works inside a class definition, as the compiler fills in the necessary details to correctly retrieve the class being defined, as well as accessing the current instance for ordinary methods.
然而,super
的单参数形式不存在这样的语句。反之:
Also note that, aside from the zero argument form, super() is not limited to use inside methods.
那么,我的问题是,幕后到底发生了什么?这是预期的行为吗?
(注意 - 我已经对这个答案进行了大量编辑,使其与实际问题更相关。)
如果调用 sup = super(A); sup.__init__()
,结果与调用 sup = super()
完全相同:在 class 定义中,您得到一个绑定的超级;在外面,你会得到一个 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!
在这两种情况下,super(A)
给出了一个未绑定的超级对象。当你在上面调用 __init__()
时,它是在没有参数的情况下调用的。当不带参数调用 super.__init__
时,编译器会尝试推断参数:(来自 typeobject.c 行 7434,最新来源)
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. */
几行之后:(同上,第 7465 行)
f = PyThreadState_GET()->frame;
...
co = f->f_code;
...
if (co->co_argcount == 0) {
PyErr_SetString(PyExc_RuntimeError,
"super(): no arguments");
return -1;
}
当您调用 super(A)
时,会绕过此推断行为,因为类型不是 None。然后当您在未绑定的 super 上调用 __init__()
- 因为它未绑定,所以此 __init__
调用未被代理 - 类型参数 是 None 并且编译器尝试推断。在 class 定义中,存在 self 参数并用于此目的。在外面,没有可用的参数,所以抛出异常。
换句话说,super(A)
而不是 根据调用位置的不同表现不同 - super.__init__()
表现不同,而这正是文档建议。