转载:Python经典的大坑问题:[lambda :x for x in range(10)] 和Python的循环作用域问题

原始链接: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
"""

====

所以,记得:返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值