Область переменных в генераторах в классах
Я думаю, что я знаю, как переменные и генераторы работают на Python.
Однако следующий код меня смущает.
from __future__ import print_function
class A(object):
x = 4
gen = (x for _ in range(3))
a = A()
print(list(a.gen))
При запуске кода (Python 2) он говорит:
Traceback (most recent call last):
File "Untitled 8.py", line 10, in <module>
print(list(a.gen))
File "Untitled 8.py", line 6, in <genexpr>
gen = (x for _ in range(3))
NameError: global name 'x' is not defined
В Python 3 говорится: NameError: name 'x' is not defined
но, когда я запускаю:
from __future__ import print_function
class A(object):
x = 4
lst = [x for _ in range(3)]
a = A()
print(a.lst)
Код не работает в Python 3, но он работает в Python 2 или в такой функции, как
from __future__ import print_function
def func():
x = 4
gen = (x for _ in range(3))
return gen
print(list(func()))
Этот код хорошо работает в Python 2 и Python 3 или на уровне модуля
from __future__ import print_function
x = 4
gen = (x for _ in range(3))
print(list(gen))
Код хорошо работает и в Python 2 и Python 3.
Почему это неправильно в class
?
Ответы
Ответ 1
Как указано в другом ответе, это True
, что это происходит, потому что это статическая переменная. Но это не просто свойство, которое ограничивает ваш код работой. Фактическая причина - область действия переменной и область ее выполнения. Например, создайте класс как:
class A(object):
x = 999999
y = x +1
Если вы получите доступ к его свойствам класса A.x
и A.y
, он будет работать. Потому что во время инициализации y
, x
заменяется значением в выражении x+1
. Поскольку область x
находилась в пределах класса.
Однако это не происходит в случае генераторов. то есть в вашем примере:
class A(object):
x = 4
gen = (x for _ in range(3))
Когда вы выполняете list(a.gen)
, он выполняется вне класса (поскольку генераторы оцениваются во время выполнения) и проверяет ссылку x
в текущей области. Поскольку x
он не инициализирован в этой области, он выдает ошибку.
Когда вы явно инициализируете x=4
, он работает, потому что теперь выражение генератора имеет значение x
, к которому оно могло бы использоваться.
Чтобы заставить ваше выражение выражений работать, как указано другими, вы должны определить его как:
class A(object):
x = 4
gen = (A.x for _ in range(3))
# ^ mentioning `A.x` is the value to access
Ответ 2
Поскольку x
- это атрибут класса (статическая переменная), к которому вы обращаетесь, например,
Пример
>>> class A(object):
... x = 4
... gen = (A.x for _ in range(3))
...
>>> a = A()
>>> list(a.gen)
[4, 4, 4]
Здесь даже gen
- еще один атрибут класса, что означает, что
>>> b = A()
>>> list(b.gen)
[]
Это дает пустое, потому что генератор уже исчерпан.
Это происходит потому, что генератор оценивается только при выпуске a.gen
, когда он не сможет разрешить имя x
.
Ответ 3
Это потому, что x
- это переменная класса. В python переменные класса должны быть доступны с помощью self
(например, из метода экземпляра) или имени класса.
class A(object):
x = 4
gen = (A.x for _ in range(3))
def call_me(self):
print self.x
a = A()
a.call_me()
print list(a.gen)
Для более подробного обсуждения см.
Статические переменные класса в Python
а также
Почему класс Python не распознает статическую переменную