理解与范围界定有着一些意想不到的互动。这是预期的行为吗?
我有个方法:
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
冒着抱怨的风险,这是错误的残酷根源。在我编写新代码时,我只是偶尔会发现由于重新绑定而导致的非常奇怪的错误——即使现在我知道这是个问题。我需要制定一个规则,比如“总是在列表理解中的临时变量前面加下划线”,但即使这样也不是傻瓜证明。
事实上,这种随机的定时炸弹等待否定了列表理解的所有“易用性”。
列表理解泄漏了Python 2中的循环控制变量,但Python 3中没有。下面是Guido van Rossum(Python的创建者)explaining这背后的历史:
是的,赋值发生在那里,就像在
for
循环中一样。没有创建新作用域。这绝对是预期的行为:在每个周期中,值都绑定到指定的名称。例如
一旦识别出这一点,似乎就很容易避免:不要对理解中的变量使用现有的名称。
是的,在Python2.x中列出理解“泄漏”它们的变量,就像for循环一样。
回想起来,这被认为是一个错误,并且通过生成器表达式避免了这个错误。EDIT:AsMatt B. notes当set和dictionary comprehension语法从Python 3后移植时,也避免了这种情况。
列表理解的行为必须保持在Python 2中的状态,但在Python 3中它是完全固定的。
这意味着:
在以下情况下,
x
始终是表达式的本地:在Python 2.x中,all将
x
变量泄漏到周围的作用域。Python 3.8的更新(?):PEP 572将引入
:=
赋值运算符,该运算符故意泄漏理解和生成器表达式之外的信息!它的动机基本上是2个用例:从早期终止的功能(如any()
和all()
)中捕获“见证”:以及更新可变状态:
请参见Appendix B了解确切范围。变量在最接近的
def
或lambda
中赋值,除非该函数声明它nonlocal
或global
。相关问题 更多 >
编程相关推荐