Почему "класс" не запускает новую область, например "def"?

Я не совсем уверен, что это для stackoverflow, поэтому, пожалуйста, поправьте меня, если нет.

то есть. скажем, у нас есть t.py с содержанием:

class A(object):
    pass
print("A:", A)

class B(object):
    print("From B: A:", A)


class OuterClass(object):
    class AA(object):
        pass
    print("AA:", AA)

    class BB(object):
        print("From BB: AA:", AA)

И теперь мы выполним его: $ python3 t.py

A: <class '__main__.A'>
From B: A: <class '__main__.A'>
AA: <class '__main__.AA'>
From BB: AA:
Traceback (most recent call last):
  File "t.py", line 9, in <module>
    class OuterClass(object):
  File "t.py", line 14, in OuterClass
    class BB(object):
  File "t.py", line 15, in BB
    print "From BB: AA:", AA
NameError: name 'AA' is not defined

Из документов:

Определение класса - это исполняемый оператор. Сначала он оценивает список наследования, если он есть. Каждый элемент в списке наследования должен оценивать объект класса или тип класса, который позволяет подклассифицировать. classs затем выполняется в новом кадре выполнения (см. раздел Именование и привязка), используя вновь созданное локальное пространство имен и оригинальное глобальное пространство имен. (Обычно набор содержит только функцию определения.) Когда класс классов завершает выполнение, его выполнение кадр отбрасывается, но его локальное пространство имен сохраняется. [4] Класс объект затем создается с использованием списка наследования для базовых классов и сохраненное местное пространство имен для словаря атрибутов. Класс имя привязано к этому объекту класса в исходном локальном пространстве имен.

Итак, я понимаю поведение, но не логическое обоснование того, что область не лексическая, как везде. Это противоречит "Особые случаи не являются достаточно сложными, чтобы нарушать правила". Почему class ведет себя иначе, чем, скажем, def?

Является ли это "практичностью бьет чистоту"? Если да, то какое оправдание? Первоначально я думал, что это может быть артефакт python 2.x, но, как вы можете видеть выше, поведение также присутствует в python 3.3.

Ответы

Ответ 1

Как заметил Wooble в комментарии, блок класса создает новую область. Проблема в том, что имена в области блока классов недоступны для областей, вложенных в эту область. Это упоминается в документации:

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

Я не могу найти источник этого права сейчас, но где-то (я думаю, по вопросу StackOverflow) я нашел одно правдоподобное обоснование для этого: если класс def был доступен во вложенных блоках, имена методов будут затенять глобальные функции, в том числе встроенных. Это создало бы неудобство для создания классов с методами с короткими простыми именами, но также с использованием встроенных функций с тем же именем. Например, вы не могли этого сделать:

class SomeDataStructure(object):
    def sum(self):
        return sum(self._data)
    def otherCalc(self):
        return sum(item for item in somethingElse)

Если блок класса находился в области видимости внутри методов, это вызовет бесконечную рекурсию, так как внутренний вызов sum имел бы доступ к имени метода и вызывал бы этот метод вместо встроенной функции sum. Аналогичным образом, другие методы, такие как метод otherCalc, больше не будут иметь доступа к глобальной функции сумм для собственного использования, но всегда будут получать метод. Ответ на другой вопрос SO описывает это, говоря "это потому, что вы должны использовать self для доступа к методам в Python".

Теперь этот аргумент действительно имеет смысл только для функций внутри классов, потому что тела функций не выполняются, когда def есть, но тела класса выполняются, когда оператор class. Однако я думаю, что если вы объедините то, что я сказал выше, с понятиями в этом сообщении в блоге, вы получите разумное обоснование. А именно, вложенные классы не были - и до сих пор не считаются стилем, который стоит поддержать. Однако функции внутри классов, конечно, обычны. Простейший способ обработки использования функции внутри внутри класса заключался в том, чтобы блок class не передавал свои имена ни в какие вложенные области. Было бы лучше, если бы он передавал свои имена вложенным классам, но не вложенные функции, но это было бы более сложным правилом, и никто не заботился бы о поддержке вложенных классов, чтобы сделать это стоящим.