I think that I know how variables and generators work in Python well.
However, the following code makes me confused.
from __future__ import print_function
class A(object):
x = 4
gen = (x for _ in range(3))
a = A()
print(list(a.gen))
When run the code (Python 2), it says:
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
In Python 3, it says NameError: name 'x' is not defined
but, when I run:
from __future__ import print_function
class A(object):
x = 4
lst = [x for _ in range(3)]
a = A()
print(a.lst)
The code doesn't work in Python 3, but it does in Python 2, or in a function like this
from __future__ import print_function
def func():
x = 4
gen = (x for _ in range(3))
return gen
print(list(func()))
This code works well in Python 2 and Python 3 or on the module level
from __future__ import print_function
x = 4
gen = (x for _ in range(3))
print(list(gen))
The code works well in Python 2 and Python 3 too.
Why is it wrong in class
?
Because x
is a class attribute (static variable), which you access like,
Example
>>> class A(object):
... x = 4
... gen = (A.x for _ in range(3))
...
>>> a = A()
>>> list(a.gen)
[4, 4, 4]
Here even gen
is another class attribute, which means that,
>>> b = A()
>>> list(b.gen)
[]
This gives empty because the generator has already exhausted.
a.gen
, when it won't be able to resolve the name x
.
As stated in other answer, it is True
that it happens because it is static variable. But it is not just that property that restricts your code to work. Actual reason is the scope of the variable and the scope in which it executes. For example, create a class as:
class A(object):
x = 999999
y = x +1
If you access it's class properties A.x
and A.y
, it will work. Because, at the time of initializing y
, x
is replaced the value in expression x+1
. As the scope of x
was within the class.
However this doesn't happens in the case of generators. i.e. in your example:
class A(object):
x = 4
gen = (x for _ in range(3))
When you do list(a.gen)
, it is executed outside the class (as generators are evaluated during run-time) and checks for the reference of x
in the current scope. Since, x
it is not initialized in that scope, it throws error.
When you explicitly initialize x=4
, it works because now the generator expression has value of x
to which it could use.
In order to make your generator expression work, as stated by others you have to define it like:
class A(object):
x = 4
gen = (A.x for _ in range(3))
# ^ mentioning `A.x` is the value to access
That is because x
is a class variable. In python, class variables has to be accessed with self
(ex. access from a instance method) or the class name.
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)
For more detailed discussion see Static class variables in Python and Why is Python class not recognizing static variable
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With