for循环中的lambda与闭包——Python的闭包是 迟绑定 , 这意味着闭包中用到的变量的值,是在内部函数被调用时查询
2023-09-14 09:11:50 时间
for循环中的lambda与闭包
问题引入
z = [lambda x:x*i for i in range(3)]
x = [o(2) for o in z]
print x # [4,4,4]
f = [lambda x: x*i for i in range(3)](与x无关)
i就是在闭包作用域(enclousing),而Python的闭包是 迟绑定 , 这意味着闭包中用到的变量的值,是在内部函数被调用时查询得到的
调用结果
>>> f = [lambda x:x*i for i in range(3)]
>>> f
[<function <listcomp>.<lambda> at 0x00000237F7DECD08>, <function <listcomp>.<lambda> at 0x00000237F7DECD90>, <function <listcomp>.<lambda> at 0x00000237F7DECE18>]
>>> f[0](3)
6 # 2 * 3
>>> f[1](3)
6 # 2 * 3
>>> f[2](3)
6 # 2 * 3
>>> f[0](1)
2 # 2 * 1
>>> f[1](1)
2 # 2 * 1
>>> f[2](1)
2 # 2 * 1
当调用 func()
时,每循环一次,将 lam 函数的地址存到 fs 中。因为在每次循环中 lam函数都未绑定 i 的值,所以直到循环结束,i 的值为2,并将 lam 中所用到的 i 值定为 2 ,因此真正调用(例如f[0](2)
)的时候 i 值保持不变(为2),如下。
表达式用普通函数表示为:
def func():
fs = []
for i in range(3):
print('进入lam前i的值:', i)
def lam(x):
print('进入lam后i的值:', i)
return x * i
fs.append(lam)
return fs
F = func()
print(F)
"""
进入lam前i的值: 0
进入lam前i的值: 1
进入lam前i的值: 2
[<function func.<locals>.lam at 0x000002B4845ACAE8>, <function func.<locals>.lam at 0x000002B4845ACB70>, <function func.<locals>.lam at 0x000002B4845ACBF8>]
"""
# 将x的值传入
for f in F:
print(f(2))
"""
进入lam后i的值: 2
4
进入lam后i的值: 2
4
进入lam后i的值: 2
4
"""
f = [lambda :i*3 for i in range(3)](与x无关)
另一种将x换成i,就与传入的x值无关了。(这里 lambda 后面什么参数都不跟)
>>> f = [lambda :i*3 for i in range(3)]
>>> f[0]()
6
>>> f[1]()
6
>>> f[2]()
6
普通函数表示如下:
def func():
fs = []
for i in range(3):
print('进入lam前i的值:', i)
def lam():
print('进入lam后i的值:', i)
return i * 3
fs.append(lam)
return fs
F = func()
print(F)
"""
进入lam前i的值: 0
进入lam前i的值: 1
进入lam前i的值: 2
[<function func.<locals>.lam at 0x000001842141CAE8>, <function func.<locals>.lam at 0x000001842141CD90>, <function func.<locals>.lam at 0x000001842141CE18>]
"""
for f in F:
print(f())
"""
进入lam后i的值: 2
6
进入lam后i的值: 2
6
进入lam后i的值: 2
6
"""
f = [lambda x, i=i:x*i for i in range(3)](与x有关)
变闭包作用域为局部作用域。其中,i=i前面的i就是局部作用域。
>>> f = [lambda x, i=i:x*i for i in range(3)]
>>> f[0](3)
0
>>> f[1](3)
3
>>> f[2](3)
6
换种写法:
def func():
fs = []
for i in range(3):
def lam(x, i=i):
return x * i
fs.append(lam)
return fs
F = func()
for f in F:
print(f(3))
"""
0
3
6
"""
f = [lambda i=i: i*i for i in range(3)]
>>> f = [lambda i=i: i*i for i in range(3)]
>>> f
[<function <listcomp>.<lambda> at 0x00000237F7DECB70>, <function <listcomp>.<lambda> at 0x00000237F7DECAE8>, <function <listcomp>.<lambda> at 0x00000237F7DECBF8>]
>>> f[0]()
0
>>> f[1]()
1
>>> f[2]()
4
上面的表达式展开如下(为了更直观,替换了变量):
def func():
fs = []
for i in range(3):
print('进入lam前i的值:', i)
def lam(x=i):
print('进入lam后i的值:', i)
return x * x
fs.append(lam)
return fs
F = func()
print(F)
"""
进入lam前i的值: 0
进入lam前i的值: 1
进入lam前i的值: 2
[<function func.<locals>.lam at 0x0000025C91F9CC80>, <function func.<locals>.lam at 0x0000025C91F9CAE8>, <function func.<locals>.lam at 0x0000025C91F9CB70>]
"""
for f in F:
print(f(8))
"""
进入lam后i的值: 2
0
进入lam后i的值: 2
1
进入lam后i的值: 2
4
"""
当调用 func()
时,每循环一次,将 lam 函数的地址存到 fs 中。但是在每次循环中 lam函数都将 i 值绑定到了 x 上,所以直到循环结束,不同地址的 lam 函数的 x 值为都不一样,因此真正调用(例如 f[0]()
)的时候 x 值都为当时被绑定的值。
但如果给 lam 函数传了参数,例如 f[0](8)
,那么所有的调用结果都为传参的平方。与上面解释并不冲突,只是将传的参数绑定到了 x 上。
>>> f = [lambda i=i: i*i for i in range(3)]
>>> f[0](8)
64
>>> f[1](8)
64
>>> f[2](8)
64
f = [lambda x=i: i*i for i in range(3)]
和第二种好像,只是变了一个字符,那么结果就大不一样了。因为局部变量是x,i是闭包,会迟绑定。
对于上面的表达式,调用结果:
>>> f = [lambda x=i: i*i for i in range(3)]
>>> f[0]()
4
>>> f[1]()
4
>>> f[2]()
4
>>> f[0](10)
4
>>> f[1](10)
4
>>> f[2](10)
4
传不传参数都不影响结果。展开后:
def func():
fs = []
for i in range(3):
def lam(x=i):
return i * i
fs.append(lam)
return fs
F = func()
for f in F:
print(f(), f(10))
"""
4 4
4 4
4 4
虽然 lam 函数将 i 的值绑定到了 x 上,但函数体中并未使用 x,所以直到循环结束,i 的值变为2,才会在调用时使用。其实同第一种情况是一样的。
相关文章
- python 高精度浮点数计算模块decimal
- Python中的浮点数原理与运算分析
- 玩转python爬虫之正则表达式
- python常用数据结构的常用操作
- 使用Python循环插入10万数据
- python pyyaml模块使用示例:读取yaml文件内容
- python程序员都在用到5个酷毙的Python工具
- Python语言学习:在python中,如何获取变量的本身字符串名字而非其值/内容及其应用(在代码中如何查找同值的所有变量名)
- Python:pmml格式文件的简介、安装、使用方法(利用python将机器学习模型转为Java常用的pmml格式文件)之详细攻略
- 已解决2.Set PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=python (but this will use pure-Python parsing and wi
- 到底该怎么学python啊?
- 盘点5种基于Python生成的个性化语音方法
- 用python对美女内容采集,舞蹈区内容真热闹~
- python for循环习题
- python可变參数调用函数问题
- python的map/reduce区别
- Python: 合并多个字典
- python 实现 softmax
- Python编程语言学习:python中浅复制/深复制(或浅拷贝/深拷贝)的简介、案例应用注意事项之详细攻略
- Python(7):判断和循环
- p7.第一章 Python基础入门 -- 分支循环语句(七)