Область имен, определенных в блоке класса, не распространяется на блоки методов.Это почему? - PullRequest
10 голосов
/ 01 марта 2012

Чтение документации Я наткнулся на следующий абзац:

Область действия определяет видимость имени в блоке. Если местный переменная определяется в блоке, ее область действия включает этот блок. Если определение происходит в функциональном блоке, область действия распространяется на любые блоки содержится внутри определяющего, если только содержащийся блок не вводит другая привязка для имени. Область имен, определенных в блок класса ограничен блоком класса; это не распространяется на кодовые блоки методов - это включает в себя понимания и генератор выражения, поскольку они реализованы с использованием области действия функции.

Я решил попробовать получить доступ к переменной класса из метода сам:

>>> class A():
    i = 1
    def f(self):
        print(i)            

>>> a = A()

>>> a.i
1

>>> a.f()
Traceback (most recent call last):
  File "<pyshell#7>", line 1, in <module>
    a.f()
  File "<pyshell#4>", line 4, in f
    print(i)
NameError: global name 'i' is not defined

Я знаю, что к переменной i можно получить доступ, явно указав имя класса A.i:

>>> a = A()
>>> class A():
    i = 1
    def f(self):
        print(A.i)          
>>> a = A()
>>> a.f()
1

Вопрос в том, почему разработчики языка сделали переменные класса невидимыми из методов? Что за этим стоит?

Ответы [ 2 ]

10 голосов
/ 01 марта 2012

Блок класса является синтаксическим сахаром для построения словаря, который затем передается метаклассу (обычно 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) и определение метода найдено.

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

  1. Функции, определенные вне класса и назначенные ему позже, должны будут использовать другой синтаксис для ссылки на атрибут класса, чем функции, определенные как часть класса.
  2. Поскольку он короче, он будет поощрять ссылку на атрибуты класса , включая статические методы и методы класса как голые имена: thing вместо использования Class.thing или self.thing. Это делает их похожими на глобальные переменные модуля, когда их нет (определения методов обычно достаточно короткие, чтобы можно было легко увидеть, что они не определены локально).
  3. Обратите внимание, что поиск атрибутов в self позволяет им лучше играть с подклассами, так как позволяет подклассам переопределять атрибут. Это, вероятно, не так уж важно для «констант классов», но это очень важно для статических методов и методов классов.

Это основные причины, которые я вижу, но в конечном итоге это просто выбор, который сделали дизайнеры Python. Вы находите странным, что у вас нет такой неявной способности ссылаться на переменные класса, но я нахожу странным доступ к переменным класса и экземпляра в таких языках, как C ++ и Java. У разных людей разные мнения.

7 голосов
/ 01 марта 2012

Похоже, это связано с использованием явного параметра self и с требованием, чтобы все вызовы методов и доступ к атрибуту экземпляра явно использовали self.Было бы по меньшей мере странно, если бы необычный случай доступа к функции области класса как к обычной функции был бы намного проще, чем обычный случай доступа к ней как к методу через self.К переменным класса обычно также обращаются через экземпляр в Python.

В C ++, напротив, область видимости класса видна во всех методах, но вызов метода неявно пропускает this.Это, кажется, другой разумный выбор.

...