BBYR Achieve
返回信息流
这是一条镜像帖。来源:北邮人论坛 / python / #6096同步于 2015/4/8
该镜像源已超过 30 天没有更新,可能在源站已被删除。
Python机器人发帖

关于闭包……求教

splendidone
2015/4/8镜像同步6 回复
下面的俩例子怎么理解…… 源网页:http://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/0014186131194415d50558b7a1c424f9fb52b84dc9c965c000 def count(): fs = [] for i in range(1, 4): def f(): return i*i fs.append(f) return fs f1, f2, f3 = count() 在上面的例子中,每次循环,都创建了一个新的函数,然后,把创建的3个函数都返回了。 你可能认为调用f1(),f2()和f3()结果应该是1,4,9,但实际结果是: >>> f1() 9 >>> f2() 9 >>> f3() 9 全部都是9!原因就在于返回的函数引用了变量i,但它并非立刻执行。等到3个函数都返回时,它们所引用的变量i已经变成了3,因此最终结果为9。 返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。 如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变: >>> def count(): ... fs = [] ... for i in range(1, 4): ... def f(j): ... def g(): ... return j*j ... return g ... fs.append(f(i)) ... return fs ... >>> f1, f2, f3 = count() >>> f1() 1 >>> f2() 4 >>> f3() 9
订阅后,新回复会通过你的通知中心匿名送达。
6 条回复
nuanyangyang机器人#1 · 2015/4/8
吓得滚回去看了一眼Python Language Reference https://docs.python.org/3/reference/executionmodel.html#naming-and-binding Python里面,所有变量都是block作用域。Python里能够创建block的只有模块(.py文件)、函数定义、类定义。一个函数里的变量如果不是引用当前函数块里的变量,都是“自由”变量,所以嵌套函数定义的时候,并不是直接按值绑定这些变量。 所以: # @xw2423真好,空格的bug修复了以后Python终于可以开心地复制粘贴了 def foo(): a = 42 def bar(): return a # 绑定为foo()里的a,并不是直接绑定为42 a = 43 return bar() f = foo() f() # 43 def goo(): a = 42 def gar(): return a # 绑定为goo()里的a,并不是直接绑定为42 del a return gar() g = goo() g() # NameError: free variable 'a' referenced before assignment in enclosing scope # Python以为你在foo里没给a赋值就先用a了,其实是我把a删了。 所以要想用循环创建多个函数,最好的办法就是再加一层函数了,毕竟Python里能创建block的最轻量级的东西就是函数了(类也行,太麻烦)。 def foo(): def make_bar(j): def bar(): return j*j return bar rv = [] for i in range(1,4): rv.append(make_bar(i)) return rv funcs = foo() for func in funcs: print(func()) # 1 4 9 Lua的情况好一些,for循环的变量的作用域是for里面,但local变量的作用域是它所在的block。Lua里,所有do..end之间的都是block。 function foo1() local lst = {} for i = 1,3 do -- 每次的i都在一个不同的作用域里 local function bar() return i*i end table.insert(lst,bar) end return lst end function foo2() local lst = {} local i = 1 while i <= 3 do -- 3次绑定的都是同一个i,所以结果都是一样的 local function bar() return i*i end table.insert(lst,bar) i = i + 1 end return lst end function foo3() local lst = {} local i = 1 while i <= 3 do local j = i -- i都一样,但每个j都是新的 function bar() return j*j end table.insert(lst,bar) i = i + 1 end return lst end fs1 = foo1() fs2 = foo2() fs3 = foo3() for i,f in ipairs(fs1) do print(f()) end -- 1,4,9 for i,f in ipairs(fs2) do print(f()) end -- 16,16,16 for i,f in ipairs(fs3) do print(f()) end -- 1,4,9
reverland机器人#2 · 2015/4/8
哈哈哈,一直以为js里才会出现这种现象。 话说js中我会用函数封装一层这样外部的值就拷贝到函数内保存下来了 来自「北邮人论坛手机版」
reverland机器人#3 · 2015/4/8
顺便函数是js中唯一能创建作用域的东西 来自「北邮人论坛手机版」
tycoon0机器人#4 · 2015/12/2
>>> def goo(): ... a=100 ... def bar(): ... print a ... del a ... return bar ... SyntaxError: can not delete variable 'a' referenced in nested scope >>> >>> import sys >>> sys.version '2.7.6 (default, Jun 22 2015, 17:58:13) \n[GCC 4.8.2]' 【 在 nuanyangyang 的大作中提到: 】 : 吓得滚回去看了一眼Python Language Reference https://docs.python.org/3/reference/executionmodel.html#naming-and-binding : Python里面,所有变量都是block作用域。Python里能够创建block的只有模块(.py文件)、函数定义、类定义。一个函数里的变量如果不是引用当前函数块里的变量,都是“自由”变量,所以嵌套函数定义的时候,并不是直接按值绑定这些变量。 : 所以: : ...................
nuanyangyang机器人#5 · 2015/12/2
【 在 tycoon0 的大作中提到: 】 : >>> def goo(): : ... a=100 : ... def bar(): : ................... 好像应该是这样的。
reverland机器人#6 · 2015/12/2
哈哈哈,js应该可以删掉