def leave_room(self, uid):
  u = self.user_by_id(uid)
  r = self.rooms[u.rid]

  other_uids = [ouid for ouid in r.users_by_id.keys() if ouid != u.uid]
  other_us = [self.user_by_id(uid) for uid in other_uids]

  r.remove_user(uid) # OOPS! uid has been re-bound by the list comprehension above

  # Interestingly, it's rebound to the last uid in the list, so the error only shows
  # up when len > 1

有鞭打的危险,这是一个残酷的错误来源。当我写新的代码,我只是偶尔发现非常奇怪的错误,由于重新绑定 – 即使现在,我知道这是一个问题。我需要一个规则像“总是前面temp vars在列表推导下划线”,但即使这不是傻瓜。


列表推导漏洞在Python 2中循环控制变量,但不是在Python 3中。这里是Guido van Rossum(Python的创造者)explaining的历史背后:

We also made another change in Python
3, to improve equivalence between list
comprehensions and generator
expressions. In Python 2, the list
comprehension “leaks” the loop control
variable into the surrounding scope:


This was an artifact of the original
implementation of list comprehensions;
it was one of Python’s “dirty little
secrets” for years. It started out as
an intentional compromise to make list
comprehensions blindingly fast, and
while it was not a common pitfall for
beginners, it definitely stung people
occasionally. For generator
expressions we could not do this.
Generator expressions are implemented
using generators, whose execution
requires a separate execution frame.
Thus, generator expressions
(especially if they iterate over a
short sequence) were less efficient
than list comprehensions.

However, in Python 3, we decided to
fix the “dirty little secret” of list
comprehensions by using the same
implementation strategy as for
generator expressions. Thus, in Python
3, the above example (after
modification to use print(x) 🙂 will
print ‘before’, proving that the ‘x’
in the list comprehension temporarily
shadows but does not override the ‘x’
in the surrounding scope.