While upgrading code from Py2 to Py3, I ran into an oddity I can't quite explain. A list comprehension in a class definition can reference other class level variables in both python 2 and 3 as part of the for clause. However, if the variables are part of the if clause they throw a NameError in python 3 but work just fine in python 2.
I found a few related questions but they don't quite seem to explain the issue. Dictionary class attribute that refers to other class attributes in the definition is a similar issue in lambdas, but doesn't seem to be python version dependant. Additonally Why am I getting a NameError in list comprehension (Python)?, but has to do with scope in a debugger rather than a class.
The following code works fine on python 2.7.16, but fails on Python 3.7.4:
class A(object):
a = [1, 2, 3]
b = [n for n in a if n in a]
print(A.b)
In python 2 I get:
[1, 2, 3]
In python 3 I get:
Traceback (most recent call last):
File "list_comprehension_closure.py", line 3, in <module>
class A(object):
File "list_comprehension_closure.py", line 5, in A
b = [n for n in a if n in a]
File "list_comprehension_closure.py", line 5, in <listcomp>
b = [n for n in a if n in a]
NameError: name 'a' is not defined
However, the following works fine in both python 2 and 3 and the only difference is the if clause:
class A(object):
a = [1, 2, 3]
b = [n for n in a]
print(A.b)
Additionally, the following works in both python 2 and 3 and the only difference is that the comprehension is defined outside of a class block:
a = [1, 2, 3]
b = [n for n in a if n in a]
print(b)
I know there were some changes to closures and list comprehensions in Python 3, but I am not seeing anything that explains this difference.
Edit: For clarity, I am not looking for a work around. As demonstrated by my last example, I'm aware that moving the variables outside of the class scope I can resolve the issue. However, what I am looking for is an explanation for why this behavior was changed in python 3.
To me this look like an issue of inappropriate scope for the variable a. The following works by putting a in the higher scope:
a = [1, 2, 3]
class A(object):
b = [n for n in a if n in a]
print(A.b)
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