Is it time to deprecate unbound super methods?

As long ago as Python 3.0, there was talk about deprecating
unbound super objects
, which aren’t documented very well, and are often confusing. Many people assume that if bound super objects dispatch to bound methods:

super(T, obj).method  # returns method bound to obj

then obviously unbound super objects must dispatch to unbound methods:

super(T).method  # returns unbound method

(I know that’s what I assumed) but that’s not the case.

Michele Simionato suggested that (1) there’s only a single use-case for unbound super objects, and (2) they don’t even work correctly for that use-case.

Now that we have the zero-argument form of super(), does that remove the motivation for unbound super objects? Should they be deprecated in 3.9 for removal in 3.10?

Or keep them and document them better?

For those that wonder about what the single-argument version does: it creates an unbound super() instance, you bind that instance, then look up attributes. E.g. store super(Type) on the class then look that up on the instance. See my answer on a Stack Overflow question.

The idea was that you’d be able to avoid repeating the class all the time:

class Foo(Bar):
    def override(self):
        return "spam" + self.__sup.override()

# set manually or have a metaclass or __init_subclass__ take care of this attribute
Foo._Foo__sup = super(Foo)

And the case were that doesn’t work is classmethods, because cls.__sup won’t be bound.

With super() now picking up the class from a closure the primary use case is gone, I wouldn’t expect anyone to be using the single-argument version.

2 Likes

I didn’t even know the single-argument version existed. The only two forms I’ve ever used are super(Type, self) and super().

1 Like

Yeah, let’s start deprecating this.

2 Likes

https://bugs.python.org/issue37808

The classmethod case will work if we update the method super.__get__ to bind an unbound super object to the argument owner when there is no argument instance to bind to. The patch of the C function super_descr_get in Objects/typeobject.c is given here as a pure Python equivalent for better readability:

    def __get__(self, instance, owner=None):
        if instance is None and owner is None:
            raise TypeError('__get__(None, None) is invalid')
-       if instance is None or self.__self__ is not None:
+       if self.__self__ is not None:
            return self
+       if instance is None:
+           return type(self)(self.__thisclass__, owner)
        return type(self)(self.__thisclass__, instance)

Demo:

>>> class A:
...     def f(self): return 'A.f'
...     @classmethod
...     def g(cls): return 'A.g'
... 
>>> class B(A):
...     def f(self): return 'B.f ' + self.__super.f()
...     @classmethod
...     def g(cls): return 'B.g ' + cls.__super.g()
... 
>>> B._B__super = super(B)  # the current broken version of super
>>> print(B().f())  # function succeeds (instance binding)
B.f A.f
>>> print(B.g())    # classmethod fails (no binding)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in g
AttributeError: 'super' object has no attribute 'g'
>>> B._B__super = Super(B)  # the proposed fixed version of super
>>> print(B().f())  # function succeeds (instance binding)
B.f A.f
>>> print(B.g())    # classmethod succeeds (class binding)
B.g A.g

So I would rather apply this patch which solves the only flaw of one-argument super than deprecate it. What do you guys think? @guido?

I have just opened a pull request providing the patch described previously: