闭包与延时绑定

855 阅读4分钟

-- 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

'''