Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't super() work with static methods other than __new__?

I understand that __new__ is a static method and super() can be called from it create a new object, like so:

>>> class A:
...     def __new__(cls):
...         print('__new__ called')
...         return super().__new__(cls)
...
>>> a = A()
__new__ called

Why doesn't the super call work with other static methods? Why does the following fail?

>>> class B:
...     @staticmethod
...     def funcB():
...         print('funcB called')
...
>>> class C(B):
...     @staticmethod
...     def funcC():
...         print('funcC called')
...         super().funcB()
...
>>> c = C()
>>> c.funcC()
funcC called
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in funcC
RuntimeError: super(): no arguments
like image 884
debashish Avatar asked Aug 04 '17 05:08

debashish


People also ask

Why super is not used in static method?

Where the "super" keyword in Java is used as a reference to the object of the superclass. This implies that to use "super" the method should be invoked by an object, which static methods are not. Therefore, you cannot use the "super" keyword from a static method.

Can we call super in static method?

Yes, you can call the methods of the superclass from static methods of the subclass (using the object of subclass or the object of the superclass).

Can this and super be used in static context?

Both this and super are non static and can not be used in static context, which means you can not use this and super keyword inside main method in Java.

Can static methods be inherited Python?

Static method definitions are unchanged even after any inheritance, which means that it can be overridden, similar to other class methods.


1 Answers

super() with no arguments in Python 3 is basically a hack over its argument based version.

When super() gets no arguments it fetches the first argument i.e the class using a special cell variable named __class__ and for second argument it will get the first local variable from the stack(which is going to be function's first argument).

In case of __new__ it can get both(__class__ and cls) and works fine.

But in this case for example there's no second variable available apart from __class__ hence it fails.

class A:
  @staticmethod
  def func():
    super().func()  # super(__class__, <missing>).func()


A().func()  # RuntimeError: super(): no arguments

Now if we change it to accept an argument then things change:

class A:
  @staticmethod
  def func(foo):
    super().func()


# This fails because super(B, 1).func() doesn't make sense.
A().func(1)  # TypeError: super(type, obj): obj must be an instance or subtype of type
# Works! But as there's no parent to this class with func() it fails as expected.
A().func(A())  # AttributeError: 'super' object has no attribute 'func'

Hence the only solution is to make the things explicit with super() in your case:

super(C, C).funcC()

In general I am not sure why the implementation in case of staticmethod can't make an exception and use __class__ for both the arguments to make it work.


Related CPython code:

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. */
        PyFrameObject *f;
        PyCodeObject *co;
        Py_ssize_t i, n;
        f = PyThreadState_GET()->frame;
        if (f == NULL) {
            PyErr_SetString(PyExc_RuntimeError,
                            "super(): no current frame");
            return -1;
        }
        co = f->f_code;
        if (co == NULL) {
            PyErr_SetString(PyExc_RuntimeError,
                            "super(): no code object");
            return -1;
        }
        if (co->co_argcount == 0) {
            PyErr_SetString(PyExc_RuntimeError,
                            "super(): no arguments");
            return -1;
        }
        ...
like image 115
Ashwini Chaudhary Avatar answered Oct 24 '22 11:10

Ashwini Chaudhary