Области классов немного странны в Python 3, но это по уважительной причине.
В Python 2 переменные итерации (i
и j
в ваших примерах) просочились из списков и будут включены в внешний объем. Это связано с тем, что они были разработаны на ранней стадии проектирования Python 2, и они были основаны на явных циклах. В качестве примера того, как это неожиданно, проверьте значения B.i
и B.j
на Python 2, где вы не получили ошибку!
В Python 3, списки исключений были изменены, чтобы предотвратить утечку. Теперь они реализованы с помощью функции (которая имеет свою собственную область), которая вызывается для создания значения списка. Это заставляет их работать так же, как генераторные выражения, которые всегда были функциями под обложками.
Следствием этого является то, что в классе понимание списка обычно не может видеть никаких переменных класса. Параллельно с тем, что метод не может напрямую видеть переменные класса (только self
или явное имя класса). Например, вызов метода в классе ниже даст тот же NameError
исключение, вы видите в списке вашего понимания:
class Foo:
classvar = "bar"
def blah(self):
print(classvar) # raises "NameError: global name 'classvar' is not defined"
Там является исключением, однако. Последовательность, повторяющаяся с помощью первого предложения for
понимания списка, оценивается вне внутренней функции. Вот почему ваш класс A
работает в Python 3. Он делает это так, чтобы генераторы могли сразу же перехватывать неистребимые объекты (а не только тогда, когда на них вызывается next
).
Но это не работает для внутреннего предложения for
в двухуровневом понимании в классе B
.
Вы можете увидеть разницу, если разобрать некоторые функции, которые создают списковые с помощью dis
модуля:
def f(lst):
return [i for i in lst]
def g(lst):
return [(i, j) for i in lst for j in lst]
Вот разборка f
:
>>> dis.dis(f)
2 0 LOAD_CONST 1 (<code object <listcomp> at 0x0000000003CCA1E0, file "<pyshell#374>", line 2>)
3 LOAD_CONST 2 ('f.<locals>.<listcomp>')
6 MAKE_FUNCTION 0
9 LOAD_FAST 0 (lst)
12 GET_ITER
13 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
16 RETURN_VALUE
Первых три строк показывают f
загрузки докомпилированный блок кода и создание функции из него (он называет его f.<locals>.<listcomp>
). Это функция, используемая для составления списка.
В следующих двух строках отображается загружаемая переменная lst
и выполненный из нее итератор. Это происходит в пределах области f
, а не внутренней функции.Затем в качестве аргумента вызывается функция <listcomp>
с этим итератором.
Это сопоставимо с классом A
. Он получает итератор из переменной класса integers
, так же, как вы можете использовать другие типы ссылок на предыдущих членов класса в определении нового члена.
Теперь сравните разборку g
, что делает пары итерации за тот же список дважды:
>>> dis.dis(g)
2 0 LOAD_CLOSURE 0 (lst)
3 BUILD_TUPLE 1
6 LOAD_CONST 1 (<code object <listcomp> at 0x0000000003CCA810, file "<pyshell#377>", line 2>)
9 LOAD_CONST 2 ('g.<locals>.<listcomp>')
12 MAKE_CLOSURE 0
15 LOAD_DEREF 0 (lst)
18 GET_ITER
19 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
22 RETURN_VALUE
На этот раз, он строит замыкание с объектом кода, а не основная функции. Закрытие - это функция с некоторыми «свободными» переменными, которые относятся к вещам в охватывающей области. Для функции <listcomp>
в g
это работает просто отлично, так как его объем является нормальным. Однако, когда вы пытаетесь использовать одно и то же понимание в классе B, закрытие не выполняется, поскольку классы не позволяют функциям, которые они содержат, видеть в своих областях таким образом (как показано выше с классом Foo
).
Стоит отметить, что причиной этой проблемы являются не только внутренние значения последовательности. Как и в previous question, связанный с помощью BrenBarn в комментариях, вы будете иметь один и тот же вопрос, если переменная класса упоминаются в другом месте в списке понимании:
class C:
num = 5
products = [i * num for i in range(10)] # raises a NameError about num
Вы, тем не менее получить ошибку из нескольких -уровневые списки, в которых внутренние аргументы for
(или if
) относятся только к результатам предшествующих циклов. Это связано с тем, что эти значения не являются частью замыкания, а только локальными переменными внутри области действия <listcomp>
.
class D:
nested = [[1, 2, 3], [4, 5, 6]]
flattened = [item for inner in nested for item in inner] # works!
Как я уже сказал, классные области немного странные.
Уверены ли вы? http://ideone.com/n87bWm – karthikr
@karthikr Кажется, связано с тем, что это переменная класса: http://ideone.com/7XfDwQ – jpmc26
Протестировано в python 3.3.2 работает нормально. – aIKid