Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does list comprehension closure throw `NameError` in python 3 but not in python 2? [duplicate]

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.

like image 414
jbryan Avatar asked Oct 23 '25 17:10

jbryan


1 Answers

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)