Почему присвоение ячейке __class__ прерывает `super`?

Я прочитал Почему в Python 3.x есть магия super ()? и понимаю, что использование super или __class__ в методе автоматически создаст переменную ячейки __class__ для этого метода:

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'>

И, насколько мне известно, ячейки используются для хранения закрывающих переменных и могут быть свободно изменены:

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>,)

Но попытка переназначить значение ячейки __class__ приводит к тому, что super выдает странную ошибку:

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

Почему это происходит? Почему нельзя переназначить __class__, как другие закрывающие переменные?

Это назначение обсуждается в ответе пользователя2357112 здесь: «Назначение __class__ означает, что __class__ является локальной переменной, а не закрывающей переменной». Дупить?

wim 20.09.2018 19:00
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
1
74
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вам понадобится оператор nonlocal для назначения закрывающим переменным, включая волшебную закрывающую переменную __class__. Назначение __class__ без оператора nonlocal создает локальную переменную, которая скрывает переменную магического закрытия.

Вы ожидаете, что __class__ будет вести себя так, как если бы он был локальным по отношению к meth, но на самом деле он ведет себя так, как если бы он был локальным по отношению к невидимой псевдо-области, в которую вложены все методы Demo. Если бы он рассматривался как локальный для meth, вам не понадобился бы nonlocal.

Если вы действительно добавите оператор nonlocal, реализация на самом деле позволит вам для переназначения переменной магического закрытия:

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

Foo().meth()

Результат:

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)

Другие вопросы по теме