有这么一个小面试题:面试
看下面代码请回答输出的结果是什么?为何?express
result = [lambda x: x + i for i in range(10)] print(result[0](10))
当你看到这篇文章的时候若是不知道这个知识点确定会拿去直接运行,输出的结果是什么呢?闭包
结果是:19app
经过result[0~9](10)结果都是19就,懵逼了吧~~函数
想知道这个咱们先看几个知识点spa
顾名思义,列表生成式就是一个用来生成列表的特定语法形式的表达式。翻译
语法格式:code
[expression for iterable_var in iterable]
工做过程:blog
工做过程相似于:it
L = [] for i in range(10): # 举例expression 表达式 iterable_var * 5 ,最后把这个结果加到列表中 L.append(i * 5)
点到为止,咱们来看下咱们的这个小面试题
result = [lambda x: x + i for i in range(10)] # 后面的lambada x:x +i 为expression 这是一个普通的lambada表达式那他生成的结果是什么? 一个一个的函数 # 看下面的例子就舒服多了 L = [] for i in range(10): L.append(lambda x: x + i)
那在咱们执行result[0](10),其实就是在执行lambda 10: 10 + i ,可是为何每一个i都是9呢?
咱们在写一个函数的时候,函数内不保存这个变量的值,而是在执行的时候去找这个值在哪里绑定上的。
举个例子来讲咱们在函数中定引用了一个变量,能够不须要提早定义咱们只要在使用前定义就能够了,以下面代码
def func(): print(foo) foo = "hello tim" func()
注:这里有个点稍微提醒下,调用函数时这个函数必须是提早定义好的,这个咱们称之为“前向引用”,而函数内部变量能够后期再定义,咱们称之为“后期绑定”
由于这个变量不是在lambda内定义的,而是在列表生成式内定义的,我先把列表生成式翻译一下:
def make_list(): L =[] for i in range(10): def inner(arg): return i + arg L.append(inner) return L result = make_list() print(result[2](10))
看上面的代码最后返回的都是一个一个的函数,而这个i的值是在循环的时候赋值的,那咱们知道这个i的值不是在,inner(lambda)函数中定义的,而是引用的上层函数的变量,当咱们使用inner去调用的时候,这时这个for循环已经结束了,i的值也就变成固定了9
因此每当咱们经过函数lambda x: x + i 的时候这个i永远是9
那若是说我不想要这样的结果,我想要i是循环的值怎么办,不要直接引用上层变量,把变量传进来就能够了
result = [lambda x, i=i, : x + i for i in range(10)] print(result[1](10))
翻译一下
def make_list(): L =[] for i in range(10): def inner(arg, i=i): return i + arg L.append(inner) return L result = make_list() print(result[2](10))
简单解释下这个概念,在嵌套函数内,嵌套函数应用上层函数的变量(不是全局变量)称之为闭包,
def func(): x = 1 def inner(): print(x)
当咱们执行func的时候就会生成一个闭包,func执行完后里面的变量x不会被回收,由于嵌套函数inner还在使用它,常见的应用场景就是咱们的装饰器