面试题

面试题

 

一 前戏: 闭包延迟绑定问题

请写出以下的代码执行结果,并解释。

def multipliers():
    return [lambda x : i*x for i in range(4)]

print ([m(2) for m in multipliers()] )
"""
[6, 6, 6, 6]
"""

 

 上述代码相当于以下这段代码。

def multipliers():

    list1 = []

    for i in range(4):
        def foo(x):
            return i * x

        # foo函数内的i在函数调用前指向循环结束最后一个i的值
        list1.append(foo)

    return list1

print(list(m(2) for m in multipliers()))

 

  为啥结果和我们意向的不一样呢,是因为循环内的函数,在执行前变量“i”都会延迟绑定,因为函数在执行之前并不会立即绑定变量i,而是等到最终for循环结束后执行内部函数才会绑定最后的i。所以才会得到我们现在看到的结果[6,6,6,6]。

 

  因为Python解释器,遇到lambda(类似于def),只是定义了一个匿名函数对象,并保存在内存中,只有等到调用这个匿名函数的时候,才会运行内部的表达式,而for i in range(4) 是另外一个表达式,需等待这个表达式运行结束后,才会开始运行lambda 函数,此时的i 指向3,x指向2。

 

二 深入: 改进

# 列表推导式,给i赋值,每次循环都指向循环的i值
def multipliers():
    return [lambda x, i=i: i * x for i in range(4)]


print([m(2) for m in multipliers()])
"""
[0, 2, 4, 6]
"""

 

上述代码相当于一下代码

# 列表推导式,给i赋值,每次循环都指向循环的i值
def multipliers():
    list1 = []

    for i in range(4):
        # 给内部函数写一个默认值,每次调用foo的时候就会先去默认参数中查找i
        def foo(x, i=i):
            return x * i
        return foo

    return list1


print([m(2) for m in multipliers()])
"""
[0, 2, 4, 6]
"""

  Python的延迟绑定其实就是只有当运行内部函数的时候,才会引用外部变量i,不运行的时候,并不是会去找i的值,这个就是第一个函数,为什么输出的结果是[6,6,6,6]的原因。

 

posted @ 2019-07-14 14:32  tank_jam  阅读(147)  评论(0)    收藏  举报