原始链接:Python经典的大坑问题:[lambda :x for x in range(10)] 和Python的循环作用域问题Python经典的大坑问题:[lambda :x for x in range(10)] 和Python的循环作用域问题 - 简书一、看代码: li = [lambda :x for x in range(10)] res = li[0]() res:9(所有都是返回9,如res = li[1]() -...https://www.jianshu.com/p/4aad4c6baeb1
本人在转载这篇文章以后,又增加了一点东东,以黄色背景色标注出来。
Python经典的大坑问题:[lambda :x for x in range(10)] 和Python的循环作用域问题
一、看代码:
li = [lambda :x for x in range(10)]
res = li[0]()
res:9(所有都是返回9,如res = li[1]() --> 9)
首先,需要解释一些基本知识:
函数在定义的时候,并没有分配内存空间用来保存任何变量的值,只有在执行的时候,才会分配空间,保存变量的值。
然后,这是一个列表解析表达式,每个元素都是一个函数,每个函数返回的是x的值。
所以,这是一个列表,有10个元素,每个元素都是一个函数,函数体是返回x的值,前面说过,没有执行的时候,x是没有值的。
所以,当去执行这个列表中的某个函数的时候,函数就去取这个x的值,那么x的值已经变为9了。因为for循环执行完毕了,x最后变成了9。
循环在python中是没有作用域的概念的,这里的10个函数都会(都可以)引用同一个x(for的那个x),所以在向列表中添加func的时候,并没有保存 i 的值,而是当执行函数( li[0]())的时候才去取,这时候循环已经结束,i 的值是 9,所以结果都是9。即是:li这个列表只是有10个函数,而每个函数体的x变量,引用的都是for的那个x,而for的这个x最后变成了9。
再进一步解释循环的作用域问题:
当 Python 遇到一个变量的话他会按照这样的顺序进行搜索:
本地作用域(Local)→外围作用域——即当前作用域是一个内嵌作用域(Enclosing locals)→全局/模块作用域(Global)→内置作用域(Built-in)。
全局作用域(Global)就是模块内的作用域,他的作用范围是单一文件内。
“for循环内的变量为何可以在for循环结束后继续被访问”:
在7. Compound statements中是这么说的:
The for-loop makes assignments to the variable(s) in the target list.
This overwrites all previous assignments to those variablees including those made in the suite of the for-loop.
…
The target list is not deleted when the loop is finished.
But if the sequence is empty, they will not have been assigned to at all the loop.
也就是说for循环中的target list(for x in range(10) x 就是target list)在循环结束中并未被删除,可以被后续程序直接使用。但除一种情况外:循环序列为空时,target list根本不会被赋值。
PYTHON的作用域由def、class、lambda等语句产生,if、try、for等语句并不会产生新的作用域。变量名引用分为三个作用域进行查找:首先是本地,然后是函数内(如果有的话),之后是全局,最后是内置。
====
本人增加的东东如下:
li = [lambda: x for x in range(3)] # print(li[0]()) for item in li: print(repr(item), ", value: ", item()) clo = item.__closure__ print(clo, ", value:", clo[0].cell_contents) """ Output: <function <listcomp>.<lambda> at 0x000002B33208C0D0> , value: 2 (<cell at 0x000002B342B33A30: int object at 0x000002B331A96950>,) , value: 2 <function <listcomp>.<lambda> at 0x000002B33239B9D0> , value: 2 (<cell at 0x000002B342B33A30: int object at 0x000002B331A96950>,) , value: 2 <function <listcomp>.<lambda> at 0x000002B342CC6550> , value: 2 (<cell at 0x000002B342B33A30: int object at 0x000002B331A96950>,) , value: 2 Process finished with exit code 0 """
====
二、也可以按我们预想的返回值:
输出:0 2 4 。就是利用闭包技术,保存 i 的值。
====
本人增加的东东如下:
from ipython_genutils.py3compat import xrange fList = [] for i in xrange(3): print("The address of i in for loop is: ", hex(id(i)), ", the value of i is: ", i) def makeFunc(i): print("The address of i in makeFunc is: ", hex(id(i)), ", the value of i is: ", i) def func(x): print("The address of i in func is: ", hex(id(i)), ", the value of i is: ", i) print("The address of x in func is: ", hex(id(x)), ", the value of x is: ", x) return x * i return func # fList.append(makeFunc(i)) currentFunc = makeFunc(i) clo = currentFunc.__closure__ print("Current closure is:", clo) fList.append(currentFunc) print("-" * 10) print("=" * 10) for f in fList: print(repr(f)) print(f(2)) print("-" * 10) """ Output: The address of i in for loop is: 0x24942136910 , the value of i is: 0 The address of i in makeFunc is: 0x24942136910 , the value of i is: 0 Current closure is: (<cell at 0x000002495344AFA0: int object at 0x0000024942136910>,) ---------- The address of i in for loop is: 0x24942136930 , the value of i is: 1 The address of i in makeFunc is: 0x24942136930 , the value of i is: 1 Current closure is: (<cell at 0x000002495344AF70: int object at 0x0000024942136930>,) ---------- The address of i in for loop is: 0x24942136950 , the value of i is: 2 The address of i in makeFunc is: 0x24942136950 , the value of i is: 2 Current closure is: (<cell at 0x000002495344AF10: int object at 0x0000024942136950>,) ---------- ========== <function makeFunc.<locals>.func at 0x00000249534514C0> The address of i in func is: 0x24942136910 , the value of i is: 0 The address of x in func is: 0x24942136950 , the value of x is: 2 0 ---------- <function makeFunc.<locals>.func at 0x00000249421C80D0> The address of i in func is: 0x24942136930 , the value of i is: 1 The address of x in func is: 0x24942136950 , the value of x is: 2 2 ---------- <function makeFunc.<locals>.func at 0x0000024953451550> The address of i in func is: 0x24942136950 , the value of i is: 2 The address of x in func is: 0x24942136950 , the value of x is: 2 4 ---------- Process finished with exit code 0 """
====
所以,记得:返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变。