Does any magic happen when I call `super(some_cls)`?

∥☆過路亽.° 提交于 2019-12-05 05:14:36

In both cases, super(A) gives an unbound super object. When you call __init__() on that, it's being called with no arguments. When super.__init__ is called with no arguments, the compiler tries to infer the arguments: (from typeobject.c line 7434, latest source)

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. */

A few lines later: (ibid, line 7465)

    f = PyThreadState_GET()->frame;
...
    co = f->f_code;
...
    if (co->co_argcount == 0) {
        PyErr_SetString(PyExc_RuntimeError,
                        "super(): no arguments");
        return -1;
    }

When you call super(A), this inferring behavior is bypassed because type is not None. When you then call __init__() on the unbound super - because it isn't bound, this __init__ call isn't proxied - the type argument is None and the compiler attempts to infer. Inside the class definition, the self argument is present and is used for this purpose. Outside, no arguments are available, so the exception is raised.

In other words, super(A) is not behaving differently depending on where it is called - it's super.__init__() that's behaving differently, and that's exactly what the documentation suggests.

(Note - I've substantially edited this answer to be more relevant to the actual question.)

If you call sup = super(A); sup.__init__(), the result is exactly the same as if you'd called sup = super(): inside a class definition, you get a bound super; outside, you get a RuntimeError (the reason for which is in my other answer).

Here's a snippet for corroboration:

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!
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!