2

Is this a python bug? Or is my understanding of the name scope wrong?

The following example uses set comprehension in the class scope:

#/usr/bin/env python
class Foo(object):
    xlist = 1,2,3,4,5,6,7,8
    xset = {xlist[idx] for idx in range(0, len(xlist), 2)}

This example raises the error

Traceback (most recent call last):
  File "./scope.py", line 2, in <module>
    class Foo(object):
  File "./scope.py", line 4, in Foo
    xset = {xlist[idx] for idx in range(0, len(xlist), 2)}
  File "./scope.py", line 4, in <setcomp>
    xset = {xlist[idx] for idx in range(0, len(xlist), 2)}
NameError: global name 'xlist' is not defined

However, the following two codes do not raise any exception:

#/usr/bin/env python
class Foo(object):
    xlist = 1,2,3,4,5,6,7,8
    xset = {idx for idx in range(0, len(xlist), 2)}


#/usr/bin/env python
class Foo(object):
    xlist = 1,2,3,4,5,6,7,8
    xset = [xlist[idx] for idx in range(0, len(xlist), 2)]

Why does python fail to recognize the first "xlist" while it recognizes the second "xlist" in the last line of the first example? Since pythion can recognize the "xlist" in the other two examples. Even the "xlist" in list comprehension can be recognized, but the "xlist" in set comprehension can not be recognized? Seems the name scope of list comprehension and set comprehension is different?

By the way, my python version is 2.7.6.

writalnaie
  • 180
  • 10
  • That post is about Python 3, where list comprehensions behave the same way as the set comprehension, but I also explain how this used to work for list comprehensions in Python 2 and why it was changed. TL;DR summary: Python 2 list comprehensions do not get their own new scope, which was considered a mistake and set and dict comprehensions *do*. Since class bodies do *not* create a new scope names in that scope are not globals and visible in the comprehension. – Martijn Pieters Mar 17 '14 at 13:01
  • @MartijnPieters Thanks for your answer! But I think your answer only explain the difference between the failed code and the second correct example. If so, why does the first correct example work? Since there is also a "xlist" in "len(xlist)" which is recognized by python. – writalnaie Mar 17 '14 at 13:24
  • The second example doesn't use `xlist` in the local scope of the `set` comprehension (the `range()` call is executed **before** the comprehensions are executed). – Martijn Pieters Mar 17 '14 at 13:27
  • It's the *loop expression* that is executed in the new scope. – Martijn Pieters Mar 17 '14 at 13:28
  • @MartijnPieters Thanks very much for your answer! What does the _loop expression_ mean here? Since I get the same error when replace the last statement in the failed code with "xset = {bar[idx] for idx in range(0, len(xlist), 2) for bar in [xlist]}". The indent is correct. if the "xlist" in "len(xlist)" is recognized, why is the second "xlist" in the second list_for still unrecognized? – writalnaie Mar 17 '14 at 13:43
  • The first part is the loop expression: `[ for in ]`; the *outermost* iterable is first evaluated before the list comprehension can run. Any further iterables (nested loops) all need to be re-evaluated for each iteration of the parent loop, so they too are evaluated in the new scope: `[ for in for in ]`. – Martijn Pieters Mar 17 '14 at 13:47
  • @MartijnPieters More precisely, how does python create the local scope on {expression for ... for ... if ... for ... } ? Every local scope for each for/if? Or only one unique local scope in __expression__? – writalnaie Mar 17 '14 at 13:47
  • One scope is created for everything but the outermost iterable. If any of those expressions use a new comprehension. – Martijn Pieters Mar 17 '14 at 13:48
  • The comprehension is like a function, with the outermost iterable as the only argument. – Martijn Pieters Mar 17 '14 at 13:49

0 Answers0