Область переменных в генераторах в классах

Я думаю, что я знаю, как переменные и генераторы работают на 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 не распознает статическую переменную