Рассмотрим эти два файла Python:
# file1.py
global_var = "abc"
class A:
x = 1
glb = global_var
y = x + 1
class B:
z = 3
glb = global_var
zz = B.z
print(f"{A.B.z=}")
print(f"{A.zz=}")
# file2.py
global_var = "abc"
class A:
x = 1
glb = global_var
y = x + 1
class B:
z = y + 1
glb = global_var
zz = B.z
print(f"{A.B.z=}")
print(f"{A.zz=}")
Можно было бы ожидать, что они сделают то же самое. Но это не так!
$ python file1.py
A.B.z=3
A.zz=3
$ python file2.py
Traceback (most recent call last):
File "file2.py", line 4, in <module>
class A:
File "file2.py", line 8, in A
class B:
File "file2.py", line 9, in B
z = y + 1
NameError: name 'y' is not defined
B может получить доступ к глобальной области, но не к области A?y = x + 1 должно работать, а z = y + 1 нет? Это дизайнерское решение или неопределенное поведение CPython?Я верю (забудьте, где это задокументировано), что все поиски в операторе class происходят в глобальной области, а не в ближайшей содержащей области. (В случае B глобальная область в любом случае является ближайшей содержащей область, потому что оператор class не создает новую область.) y не определен, потому что определение B не смотрит в определение A.
@chepner Это то, чего я ожидал, но печать locals().keys() в определениях A и B действительно показывает соответствующие переменные класса в локальной области видимости. И на самом деле, если подумать, так и должно быть, иначе все переменные класса оказались бы в области видимости модуля. Таким образом, область класса есть, но это временная область, которая существует только при построении класса.
Нет, есть пространство имен классов; это не то же самое, что масштаб. locals возвращает это пространство имен, но это не означает, что разрешение имен использует его.
Связано (возможно, обман): Доступ к переменным класса из понимания списка в определении класса
Соответствующая часть из PEP 227: Имена в области класса недоступны. Имена разрешаются в самой внутренней области объемлющей функции. Если определение класса встречается в цепочке вложенных областей, процесс разрешения пропускает определения классов.






Из https://docs.python.org/3/reference/executionmodel.html:
Блоки определения класса и аргументы для exec() и eval() имеют особое значение в контексте разрешения имен. Определение класса — это исполняемый оператор, который может использовать и определять имена. Эти ссылки следуют обычным правилам разрешения имен, за исключением того, что несвязанные локальные переменные просматриваются в глобальном пространстве имен. Пространство имен определения класса становится словарем атрибутов класса. Объем имен, определенных в блоке класса, ограничен блоком класса; он не распространяется на блоки кода методов — это включает в себя включения и выражения генератора, поскольку они реализованы с использованием области действия функции. Это означает, что следующее не удастся:
В определении By является несвязанной локальной переменной, поэтому поиск осуществляется в глобальной области видимости (где она не определена), а не в пространстве имен, созданном прилагаемым оператором class.
Оператор class вообще не определяет область действия; он создает пространство имен, которое передается метаклассу для создания нового класса.
Не совсем верно, что области видимости класса всегда ищут несвязанные локальные переменные в глобальном пространстве имен. Они могут получать доступ к переменным замыкания, но, как указано чуть позже в цитате, область видимости внешнего класса не является допустимой областью замыкания.
В вашем примере x не является несвязанной локальной переменной, я думаю, из-за закрытия.
Замыкания находятся в функциях, а не в классах
Чепнер Уже ответил на этот вопрос, но вкратце: локальные переменные просматриваются в глобальном пространстве имен, поэтому способ вложения классов в ваш пример кода работает, как и ожидалось.
Он работает так, как ожидалось, как указано выше.
Они не работают, как для вложенных функций с правилом LEGB /realpython:
«Когда вы определяете класс, вы создаете новую локальную область Python. Имена, назначенные на верхнем уровне класса, живут в этой локальной области. Имена, которые вы назначаете внутри оператора класса, не конфликтуют с именами в других местах. Можно сказать, что эти имена следуют правилу LEGB, где блок класса представляет уровень L».
Также не очень распространено вложение класса в другой класс, обычно вы выполняете наследование класса следующим образом:
class B(A):
y = A.y
z = y + 1
glb = global_var
Это происходит по той же причине, по которой вы должны использовать
self.xилиClassName.xдля доступа к переменной классаxв методе.