What is the difference between super() and explicit super(Cl,self) (with __slots__ and attrs)

python, python-3.x, python-attrs, super

Solution

`super()` normally relies on the compiler to provide a `__class__` closure cell, that is bound to the class object a method is derived on. The closure is created the moment you use the name `super()` in a method (or if you used `__class__`):

>>> class Foo(object):
...     def bar(self):
...         super()  # just using super or __class__ is enough
...
>>> Foo.bar.__closure__[0].cell_contents
<class '__main__.Foo'>
>>> Foo.bar.__closure__[0].cell_contents is Foo
True

That closure lets `super()` work without arguments (the `self` argument is taken from the local namespace).

However, `attr` generates a new class object when you specify you wanted to use `__slots__`; you can't add slots to a class after the fact, so a new class object is created that replaces the one you decorated.

The closure attached to `meth` is the original pre-decoration class and not the same class object as the new generated class:

>>> Derived.meth.__closure__[0].cell_contents
<class '__main__.Derived'>
>>> Derived.meth.__closure__[0].cell_contents is Derived
False

This breaks the expectations `super()` has, making it impossible to use the 0 argument variant. The `super(Derived, self)` variant explicitly looks up the name `Derived` as a global when called, finding the new generated class, so works.

I go into some more detail on how `super()` without arguments works, and why, in Why is Python 3.x's super() magic?

This was reported as issue #102 in the tracker, and fixed by altering the closure with some `ctypes` hackery. This fix will be part of the upcoming 17.3 release.

Problem

I'm using the `attrs` python package, in combination with inheritance and slots. I want to call the parent class's method from within the derived method. The problem is demonstrated below: ``` import attr @attr.s(slots=True) class Base(): def meth(self): print("hello") @attr.s(slots=True) class Derived(Base): def meth(self): super().meth() print("world") d = Derived() d.meth() ``` I get: TypeError: super(type, obj): obj must be an instance or subtype of type The problem seems to be triggered by a combination of attrs (undecorated classes with explicit `__slots__=()` work), slots (regular `@attr.s`-decorated classes work) and the plain `super()` call (`super(Derived, self)` works). I would like to understand how `super()` behaves differently from the explicit `super(Derived, self)` version, since the documentation says they "do the same thing"

Original source

Related problems