5

have a look at the following piece of code:

class a:
    s = 'python'
    b = ['p', 'y']
    c = [x for x in s]

the output:

>>> a.c
['p', 'y', 't', 'h', 'o', 'n']

but when i try to limit the list with if:

class a:
    s = 'python'
    b = ['p', 'y']
    c = [x for x in s if x in b]

Shows the following exception:

Traceback (most recent call last):
  File "<pyshell#22>", line 1, in <module>
    class a:
  File "<pyshell#22>", line 4, in a
    c = [x for x in s if x in b]
  File "<pyshell#22>", line 4, in <listcomp>
    c = [x for x in s if x in b]
NameError: global name 'b' is not defined

If a make global b it works, why this is happening?

davidism
  • 121,510
  • 29
  • 395
  • 339
Marco Herrarte
  • 1,540
  • 5
  • 21
  • 42

1 Answers1

7

This isn't so much about the variable scope in list comprehensions as about the way classes work. In Python 3 (but not in Python 2!), list comprehensions don't affect the scope around them:

>>> [i for i in range(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> i
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'i' is not defined
>>> i = 0
>>> [i for i in range(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> i
0

However, when you do that in a class, it won't look for b among the class attributes the way it would in the local scope of a module or function. To do what you are trying to do, use the @property decorator:

>>> class a:
...   s = 'python'
...   b = 'py'
...   @property
...   def c(self):
...     return [x for x in self.s if x in self.b]
... 
>>> A = a()
>>> A.c
['p', 'y']

Also, remember that strings are iterable too (they're just lists of their component characters), so no need to explicitly make b a list.

Community
  • 1
  • 1
Two-Bit Alchemist
  • 17,966
  • 6
  • 47
  • 82
  • Thank you!! b is a list above cause it will have not precisely a string :D thanks again!!! – Marco Herrarte Apr 01 '14 at 22:21
  • 1
    @grkiran2011 (new user) raised the following question: _Sorry but could not understand the explanation provided. I mean how can the list comprehension see the string s, but not the list b. Could you please clarify._ – kvantour Mar 06 '18 at 08:53
  • 1
    @kvantour the contexts are slightly different: in the part where it "zooms" on a specific element it doesn't see the stuff from the enclosing scope (that's the `if ...` part here). It however does see those external variables where the list comprehension is fed the container to work with. – bawey Mar 08 '21 at 16:42