-- coding:utf-8 --
什么是闭包
1闭包是一个嵌套函数
2闭包必须返回嵌套函数
3嵌套函数必须引用一个外部的非全局的局部自由变量
这不是闭包
'''
def foo1():
print('嘿嘿')
def foo2():
print('哈哈')
foo2()
'''
这是闭包
'''
def foo1(n):
def foo2(m):
return n + m
return foo2
a = foo1(2)
print(a(3))
'''
这也是闭包
'''
def foo1():
var = 'hello'
def foo2():
print(var)
return foo2
a = foo1()
a()
'''
python的延时绑定
''' def multipliers(): return [lambda x : i*x for i in range(4)]
print [m(2) for m in multipliers()]
output:
[6, 6, 6, 6]
def multipliers(): return [lambda x : i*x for i in range(4)]
这个是一个闭包:因为满足我们的条件啊 1嵌套、2引用了局部变量、3返回了该嵌套函数
为毛结果是[6, 6, 6, 6] 而不是我们以为的[0,2,4,6]呢?
我们可以理解的点: def multipliers(): return [lambda x : i*x for i in range(4)]
把他拆开:
def multipliers(): def lam(x): return xi return lam 这个xi是变化的吗? 显然不是 因为他大爷i就没变过。
函数执行顺序是这样的: 1 for i in range(4) 2 返回一个列表,列表里面的元素是4个函数对象(严格上来讲是四个函数计算之后得出的值的对象) 3 当我们执行这一行代码的时候 print [m(2) for m in multipliers()] 开始调用我们的匿名函数了,这个时候匿名函数需要一个值,四次都是一个值,不是四次四个值哦~ 他去for i in range(4)返回的结果里找。找离他最近的 只能是3 为什么不是2,1,0呢? 因为for循环循环过了,都是前女友了。 所以实际上 def multipliers(): return [lambda x : i*x for i in range(4)] 的返回值是
为了便于理解,你可以想象下multipliers内部是这样的(这个是伪代码,并不是准确的):
def multipliers(): return [lambda x: 3 * x, lambda x: 3 * x, lambda x: 3 * x, lambda x: 3 * x]
而不是!! def multipliers(): return [lambda x: 0 * x, lambda x: 1 * x, lambda x: 2 * x, lambda x: 3 * x]
看看大神是怎么解释的吧:(敲黑板,同学们!) 因为Python解释器,遇到lambda(类似于def),只是定义了一个匿名函数对象,并保存在内存中,只有等到调用这个匿名函数的时候, 才会运行内部的表达式,而for i in range(4) 是另外一个表达式,需等待这个表达式运行结束后,才会开始运行lambda 函数, 此时的i 指向3,x指向2
总结一下: Python的延迟绑定其实就是只有当运行嵌套函数的时候,才会引用外部变量i,不运行的时候,并不是会去找i的值,这个就是第一个函数,为什么输出的结果是[6,6,6,6]的原因。 '''
''' 什么? 你还不明白? 没关系 来看下面
分析原代码前先分析一下列表表达式
使用列表表达式
li = [x for x in range(4)] print("列表表达式:",end="") print(li)
探究列表表达式本源
li.clear() for x in range(4): li.append(x) print("循环构建列表:",end="") print(li)
分析 [lambda x:i*x for i in range(4)]
li.clear() for i in range(4): def lamb(x): return i*x li.append(lamb)
都是函数对象
print("输出构造结果:",end="") print(li)
因为并且每个lamb函数构造完成是这样
def lamb(2):
return i*2
使用的i是一个lamb外部的匿名变量 所以产生以下结果
print("共享i的结果:[",end=" ") for fun in li: print(fun(2),end=" ") else: print("]")
思考一下? 如何做到[0,2,4,6]呢? 一年后...
如果需要产生0,2,4,6结果需要把i作为函数局部变量
第一种 构建局部变量
li.clear() for i in range(4): # 将i的值作为局部变量(函数的参数) 指定默认值是外部的i def lamb(x,i=i): return i*x li.append(lamb)
输出
print("使用局部i的结果:[",end=" ") for fun in li: print(fun(2),end=" ") else: print("]")
第二种 使用生成器
li.clear() def fun(): for i in range(4): def lamb(x): return i*x yield lamb
迭代输出
print("使用生成器结果:[",end=" ") for lamb in iter(fun()): print(lamb(2),end=" ") else: print("]")
回顾原函数
def mul(): return [lambda x:i*x for i in range(4)]
print([m(2) for m in mul()])
转化后
print("[",end=" ") for m in [lambda x:i*x for i in range(4)]: print(m(2),end=" ") else: print("]")
综上所述产生[6,6,6,6],核心点是因为列表表达式产生函数列表时使用共享的匿名外部变量i
'''