This question already has an answer here:
Here I'm not able to access the class variable inside a Python's list comprehension.
class Student:
max_year = 18
year_choice = [i for i in range(100) if i > max_year]
def __init__(self, name):
self.name = name
print (self.year_choice)
Student('Blah')
But it's working fine in Python 2.
../workspace$ python student.py
[19, 20, 21, 22, 2.... 99]
But getting an error in Python 3.
../workspace$ python student.py
File "student.py", line 18, in <listcomp>
year_choice = [i for i in range(100) if i > max_year]
NameError: name 'max_year' is not defined
from debugging this When I changed below statement
[i for i in range(100) if i > max_year]
to this
[i for i in range(max_year)] # don't look too much into it ;)
working fine. Why I'm not able to access class variable inside if/else list comprehension?
The reason that this line
works in Python 2 but not in Python 3 is that in Python 3 list comprehensions create a new scope, and the
max_year
class attribute isn't in that scope. In Python 2, a list comprehension doesn't create a new scope, it runs in the context of the surrounding code. That was originally done for performance reasons, but a lot of people found it confusing, so it was changed in Python 3, bringing list comprehensions into line with generator expressions, and set and dict comprehensions.AFAIK, there is no simple way in Python 3 to access a class attribute inside a list comprehension that is running in the outer context of a class, rather than inside a method. You can't refer to it with
Student.max_year
, since at that point theStudent
class doesn't exist.However, there really is no point having that list comprehension there anyway. You can create the list you want more compactly, and more efficiently. For example:
output
That code produces the same output on Python 2 and Python 3.
I've changed the class signature to
so that it creates a new-style class in Python 2 (in Python 3 all classes are new-style).
The reason that
[i for i in range(max_year)]
can get around this restriction is that an iterator is created fromrange(max_year)
which is then passed as the argument to the temporary function which runs the list comprehension. So it's equivalent to this code:Many thanks to Antti Haapala for this explanation.