面试题答案
一键面试错误代码及分析
def outer():
result = []
for i in range(3):
result.append(lambda x: x * i)
return result
funcs = outer()
for func in funcs:
print(func(5))
这段代码中,outer
函数返回一个由lambda
表达式组成的列表。本意是让每个lambda
表达式将传入参数x
与i
相乘,i
在循环过程中依次为0, 1, 2。但实际执行时,lambda
表达式中的i
在调用时才取值,此时循环已经结束,i
的值为2。所以每个lambda
表达式执行的结果都是5 * 2 = 10
。这是因为lambda
表达式形成了闭包,它延迟绑定变量,在调用时才去查找变量的值,而不是在定义时就确定值。
正确实现
def outer():
result = []
for i in range(3):
result.append(lambda x, j = i: x * j)
return result
funcs = outer()
for func in funcs:
print(func(5))
在这个正确实现中,通过在lambda
表达式的参数列表中使用默认参数j = i
,使得lambda
表达式在定义时就绑定了i
的值,而不是在调用时才去查找i
的值。这样每个lambda
表达式分别绑定了i
的不同值,即0, 1, 2,所以输出结果为0, 5, 10。
lambda
表达式、闭包与作用域关系解释
lambda
表达式:是一种匿名函数,简洁地定义小型函数。在上述代码中,用于创建简单的乘法函数。- 闭包:由函数和与其相关的引用环境组合而成的实体。
lambda
表达式形成了闭包,它可以访问其定义时所在的外部作用域的变量。在错误代码中,lambda
表达式访问了outer
函数中的i
,但由于延迟绑定,导致结果不符合预期。 - 作用域:在Python中,变量的作用域决定了变量的可见性和生命周期。
lambda
表达式所在的作用域链包括其自身的局部作用域、outer
函数的局部作用域等。当lambda
表达式访问变量时,会沿着作用域链查找,在错误代码中,lambda
表达式在调用时沿作用域链找到的i
是循环结束后的最终值。而在正确实现中,通过默认参数的方式,在lambda
表达式定义时就确定了变量的值,避免了因作用域查找规则导致的问题。