python中奇怪的闭包行为

gri*_*yvp 13 python closures

我有一个简单的代码:

def get():
    return [lambda: i for i in [1, 2, 3]]

for f in get():
    print(f())
Run Code Online (Sandbox Code Playgroud)

正如我的python知识预期的那样,输出为3 - 整个列表将包含最后一个值i.但这是如何在内部工作的?

AFAIK,python变量只是对象的引用,因此第一个闭包必须包含对象的第一个i引用 - 这个对象肯定是1,而不是3 O_O.如何发生python闭包包含变量本身而不是对象这个变量引用?它是否将变量名称保存为纯文本,某些"变量引用"还是什么?

jdi*_*jdi 13

正如@ thg435指出的那样,lambda不会在那一刻封装值,而是封装范围.有太小的方法可以解决这个问题:

lambda默认参数"hack"

[ lambda v=i: v for i in [ 1, 2, 3 ] ]
Run Code Online (Sandbox Code Playgroud)

或者使用functools.partial

from functools import partial
[ partial(lambda v: v, i) for i in [ 1, 2, 3 ] ]
Run Code Online (Sandbox Code Playgroud)

基本上,您必须将范围移动到您正在创建的函数的本地.一般来说,我partial更喜欢使用它,因为你可以传递一个可调用的,以及任何args和kargs来创建一个具有适当闭包的callable.在内部,它正在包装您的原始可调用对象,因此范围会为您移动.

  • +1在这里使用部分,我认为这是一个更清洁的方法.您可能想要编辑代码,根据PEP-8,列表括号内的空格不好.(我知道你跟着提问者,我在那里编辑过). (5认同)

geo*_*org 10

闭包不是指变量,而是指范围.由于i其范围中的最后一个值为"3",因此所有三个闭包都返回相同的值.要"锁定"变量的当前值,请为其创建一个新范围:

def get() : return [ (lambda x: lambda: x)(i) for i in [ 1, 2, 3 ] ]
for f in get() : print( f() )
Run Code Online (Sandbox Code Playgroud)

  • @EyeofHell:我认为[pep-227](http://www.python.org/dev/peps/pep-0227/)是关于python范围规则的规范文档.此外,这里有一些很好的答案,例如[这里](http://stackoverflow.com/questions/291978/short-description-of-python-scoping-rules) (3认同)
  • `(lambda x:lambda:x)(i)`是我在Python中看过的最丑陋的东西之一.伊克.(并不是说你的答案是错误的或者坏的,本身,只是说 - 这很难读). (2认同)
  • @GregE.我不认为在Python中做得很好是不可能的.我认为jdi对`functools.partial()`和`lambda` mixed的回答是最好的解决方案,它从我的角度描述了以更好的方式完成的工作. (2认同)