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"