2

This works:

class Foo:
    ATTR_1 = [1, 2]
    ATTR_2 = [i for i in ATTR_1]

    def __init__(self):
        print(Foo.ATTR_1, Foo.ATTR_2)
Foo()

It prints [1, 2] [1, 2]. However, adding a condition to the list comprehension in the third line causes Python to throw a NameError:

class Foo:
    ATTR_1 = [1, 2]
    ATTR_2 = [i for i in ATTR_1 if len(ATTR_1) > 0]

    def __init__(self):
        print(Foo.ATTR_1, Foo.ATTR_2)
Foo()

Traceback:

Traceback (most recent call last):
  File "/path/to/tmp.py", line 1, in <module>
    class Foo:
  File "/path/to/tmp.py", line 3, in Foo
    ATTR_2 = [i for i in ATTR_1 if len(ATTR_1) > 0]
  File "/path/to/tmp.py", line 3, in <listcomp>
    ATTR_2 = [i for i in ATTR_1 if len(ATTR_1) > 0]
NameError: name 'ATTR_1' is not defined

Why is that and how can I work around it? I am using Python 3.6.4.

Kilian Obermeier
  • 6,678
  • 4
  • 38
  • 50
  • 3
    It's an interesting question, but I think this is mostly addresssed here: https://stackoverflow.com/questions/13905741/accessing-class-variables-from-a-list-comprehension-in-the-class-definition – Mark Jul 25 '19 at 13:51
  • 1
    Thanks for the hint! I am happy to see that there already is an excellent answer to this question and will flag it as a duplicate. – Kilian Obermeier Jul 25 '19 at 13:55
  • 1
    I was just writing up an answer that demonstrates the difference between the two uses of `ATTR_1`, so I'll comment instead. `ATTR_1` is a "local" name when used to get the iterator for the comprehension, but it is treated as a *global* in the anonymous function that implements the comprehension. See the output of `dis.dis('[i for i in ATTR_1 if len(ATTR_1) > 0]')` to see the difference. – chepner Jul 25 '19 at 13:59
  • 1
    @KilianBatzner You can define ATTR_1 as global outside of class: global ATTR_1 ATTR_1= [1, 2] Foo() print(ATTR_1) class Foo: ATTR_2 = [i for i in ATTR_1 if(len(ATTR_1)>0)] def __init__(self): print(Foo.ATTR_2) – Mahsa Hassankashi Jul 25 '19 at 14:21

0 Answers0