Блок класса является синтаксическим сахаром для построения словаря, который затем передается метаклассу (обычно type
) для создания объекта класса.
class A:
i = 1
def f(self):
print(i)
Примерно эквивалентно:
def f(self):
print(i)
attributes = {'f': f, 'i': 1)
A = type('A', (object,) attributes)
С этой точки зрения, нет внешней области, из которой должно исходить имя i
. Однако, очевидно, существует временная область для выполнения операторов в блоке класса. Было бы возможно , чтобы этот блок классов перешел на что-то более похожее на:
def attributes():
i = 1
def f(self):
print(i)
return locals()
A = type('A', (object,), attributes())
В этом случае будет работать внешняя ссылка на i
. Тем не менее, это будет идти "вразрез" философии объектной системы Python.
В Python есть объекты, которые содержат атрибуты. На самом деле в функциях не существует понятия «переменных», кроме локальных переменных (которые могут быть вложены для создания цепочки областей действия). Голое имя рассматривается как локальная переменная, а затем во внешних областях (которые поступают из функций). Атрибуты ищутся с использованием синтаксиса точечного имени в других объектах, и вы всегда указываете, какой объект искать.
Существует протокол для разрешения ссылок на атрибуты, в котором говорится, что когда attribute
не найден в obj
, obj.attribute
можно разрешить, посмотрев в класс obj
(и его базовые классы, используя порядок разрешения метода). Это на самом деле, как методы найдены; когда в вашем примере вы выполнили a.f()
, объект a
не содержит атрибута f
, поэтому поиск класса a
(то есть A
) и определение метода найдено.
Наличие атрибутов класса, автоматически доступных во внешней области видимости для всех методов, было бы странно, потому что никакой другой атрибут не работает таким образом. Это также будет иметь следующие недостатки:
- Функции, определенные вне класса и назначенные ему позже, должны будут использовать другой синтаксис для ссылки на атрибут класса, чем функции, определенные как часть класса.
- Поскольку он короче, он будет поощрять ссылку на атрибуты класса , включая статические методы и методы класса как голые имена:
thing
вместо использования Class.thing
или self.thing
. Это делает их похожими на глобальные переменные модуля, когда их нет (определения методов обычно достаточно короткие, чтобы можно было легко увидеть, что они не определены локально).
- Обратите внимание, что поиск атрибутов в
self
позволяет им лучше играть с подклассами, так как позволяет подклассам переопределять атрибут. Это, вероятно, не так уж важно для «констант классов», но это очень важно для статических методов и методов классов.
Это основные причины, которые я вижу, но в конечном итоге это просто выбор, который сделали дизайнеры Python. Вы находите странным, что у вас нет такой неявной способности ссылаться на переменные класса, но я нахожу странным доступ к переменным класса и экземпляра в таких языках, как C ++ и Java. У разных людей разные мнения.