3

Need help on why this code snippet does not return as I'd expect

>>> a = 1
>>> v = ["a", "b", "c"]
>>> {e for e in v if locals().get(e) is None}
set(['a', 'c', 'b'])

I expected it to return set(['c', 'b']), just like if I build a list

>>> [e for e in v if locals().get(e) is None]
['b', 'c']
martineau
  • 119,623
  • 25
  • 170
  • 301
zhaomin
  • 381
  • 3
  • 13
  • 1
    Closely related: [Python list comprehension rebind names even after scope of comprehension. Is this right?](//stackoverflow.com/q/4198906) – Martijn Pieters May 23 '18 at 14:57
  • 1
    Near-duplicate: [Python dictionary comprehension gives KeyError](//stackoverflow.com/q/22485399) – Aran-Fey May 23 '18 at 15:05

1 Answers1

6

In Python 2, set and dictionary comprehensions have their own scope; locals() inside such a construct refers to that new nested scope.

List comprehensions do not, because they were implemented earlier in the language lifecycle before the developers realised that a new scope would be a much better idea. In Python 3, list comprehensions have their own scope too.

You can work around this by creating a single reference to the dictionary that locals() returns before running your set comprehension:

>>> l = locals()
>>> {e for e in v if l.get(e) is None}
{'c', 'b'}
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343