最近我开始玩Python,我遇到了一些特殊的闭包方式.请考虑以下代码:
adders=[0,1,2,3]
for i in [0,1,2,3]:
adders[i]=lambda a: i+a
print adders[1](3)
Run Code Online (Sandbox Code Playgroud)
它构建了一个简单的函数数组,它接受单个输入并返回由数字添加的输入.函数在for循环中构造,迭代器i从中循环0到3.对于这些数字中的每一个,lambda都会创建一个函数i,该函数捕获并将其添加到函数的输入中.最后一行将第二个lambda函数3作为参数调用.令我惊讶的是输出结果是6.
我期待一个4.我的理由是:在Python中,一切都是一个对象,因此每个变量都是指向它的指针.在创建lambda闭包时i,我希望它存储一个指向当前指向的整数对象的指针i.这意味着当i分配一个新的整数对象时,它不应该影响先前创建的闭包.遗憾的是,adders在调试器中检查数组表明它确实存在.所有的lambda功能指的最后一个值i,3,这将导致adders[1](3)返回6.
这让我想知道以下内容:
lambda函数以更改其值i时不会受到影响的方式捕获当前i值?理解与范围界定有一些意想不到的相互作用.这是预期的行为吗?
我有一个方法:
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
Run Code Online (Sandbox Code Playgroud)
冒着抱怨的风险,这是一个残酷的错误来源.当我编写新代码时,我偶尔会发现由于重新绑定而导致非常奇怪的错误 - 即使现在我知道这是一个问题.我需要制定一个规则,比如"总是用下划线列出列表推导中的临时变量",但即使这样也不是万无一失的.
这种随机定时炸弹等待的事实否定了列表理解的所有"易用性".