NameError in nested comprehensions [duplicate]

2019-01-15 18:20发布

问题:

This question already has an answer here:

  • Accessing class variables from a list comprehension in the class definition 4 answers

I have this code in a file

class Sudoku(dict):
    COLUMNS = [
        {(x, y) for y in xrange(9)} for x in xrange(9)
    ]

When I run python broken.py, I get the traceback:

Traceback (most recent call last):
  File "U:\broken.py", line 1, in <module>
    class Sudoku(dict):
  File "U:\broken.py", line 3, in Sudoku
    {(x, y) for y in xrange(9)} for x in xrange(9)
  File "U:\broken.py", line 3, in <setcomp>
    {(x, y) for y in xrange(9)} for x in xrange(9)
NameError: global name 'x' is not defined
[Finished in 0.1s with exit code 1]

I don't really see the problem here. Isn't x defined in the comprehension?


What's stranger is how this seems to execute without an error when pasted directly into the python interpreter...


EDIT: This works if I use a list comprehension rather than a set comprehension

回答1:

I've filed a bug here. This is still broken by design in python 2.7.5.

From the bug report:

In Python 2, list comprehensions don't have their own scope, so the x in your initial example lives at class scope. However, the set comprehension does have its own scope. By design, a variable defined at class scope is not visible to inner scopes inside that class.

In Python 3, this works because the list comprehension has its own scope.



回答2:

Wild guess, but Python set comprehensions were introduced with Python 2.7 and 3.0: would you happen to use an older version to execute your script, and a recent one as your interpreter ?


After looking at this section of the Python documentation, I see no explanation for this behavior. Furthermore, the fact that it works with list comprehension clearly show that it is not a scope issue.

I see only two possible reasons:

  • A bug in the set comprehensions implementation
  • The use of an anterior Python version which does not support set comprehensions.


回答3:

I'm afraid I have no idea why your code is not working, however, the following works and gives you what you want:

class Sudoku(dict):
    COLUMNS = [
        set([(x, y) for y in xrange(9)]) for x in xrange(9)
    ]

Maybe some of the python gurus on this site can enlighten us as to why your code snippet fails.



回答4:

I wish I could give a theoretical explanation, but this works:

class Sudoku(dict):
    def __init__(self):
        self.COLUMNS = [
            {(x, y) for y in xrange(9)} for x in xrange(9)
            ]

if __name__ == "__main__":
    s = Sudoku()
    print s.COLUMNS


回答5:

Maybe this it what you really want:

[[{x:y} for x in xrange(9)] for y in xrange(9)]