1

I've read Why is Python 3.x's super() magic? and understand that using super or __class__ in a method will automagically create a __class__ cell variable for that method:

class Demo:
    def meth(self):
        super().meth()
>>> Demo.meth.__closure__
(<cell at 0x7f4572056138: type object at 0x564bda0e5dd8>,)
>>> Demo.meth.__closure__[0].cell_contents
<class '__main__.Demo'>

And as far as I know, cells are used to hold closure variables and can be freely modified:

def outer():
    x = 3
    def inner():
        print(x)

    x = 5
    return inner

inner = outer()
inner()  # output: 5
>>> inner.__closure__
(<cell at 0x7f2183a5e138: int object at 0x7f2184600460>,)

But trying to reassign the value of the __class__ cell makes super throw a strange error:

class Demo:
    def meth(self):
        __class__ = Demo
        super().meth()

Demo().meth()
Traceback (most recent call last):
  File "untitled.py", line 8, in <module>
    Demo().meth()
  File "untitled.py", line 6, in meth
    super().meth()
RuntimeError: super(): __class__ cell not found

Why does this happen? Why can't __class__ be reassigned like other closure variables?

Aran-Fey
  • 39,665
  • 11
  • 104
  • 149
  • 1
    This assignment is discussed in user2357112 answer [here](https://stackoverflow.com/a/36994027/674039): "The assignment to `__class__` means `__class__` is a local variable instead of a closure variable". Dupe? – wim Sep 20 '18 at 17:00

1 Answers1

2

You need a nonlocal statement to assign to closure variables, including the magic __class__ closure variable. Assigning to __class__ without a nonlocal statement creates a local variable that hides the magic closure variable.

You're expecting __class__ to behave as if it was local to meth, but it actually behaves as if it's local to an invisible pseudo-scope in which all methods of Demo are nested. If it was treated as local to meth, you wouldn't need nonlocal.

If you do add a nonlocal statement, the implementation actually will allow you to reassign the magic closure variable:

class Foo:
    def meth(self):
        nonlocal __class__
        __class__ = 3
        super()

Foo().meth()

Result:

Traceback (most recent call last):
  File "./prog.py", line 7, in <module>
  File "./prog.py", line 5, in meth
RuntimeError: super(): __class__ is not a type (int)
user2357112
  • 260,549
  • 28
  • 431
  • 505