惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Blog — PlanetScale
Blog — PlanetScale
博客园_首页
WordPress大学
WordPress大学
博客园 - 聂微东
P
Privacy International News Feed
Forbes - Security
Forbes - Security
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Last Week in AI
Last Week in AI
C
CERT Recently Published Vulnerability Notes
月光博客
月光博客
NISL@THU
NISL@THU
美团技术团队
T
Tailwind CSS Blog
Jina AI
Jina AI
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Apple Machine Learning Research
Apple Machine Learning Research
C
Cisco Blogs
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
The Hacker News
The Hacker News
B
Blog
P
Palo Alto Networks Blog
L
Lohrmann on Cybersecurity
有赞技术团队
有赞技术团队
The Register - Security
The Register - Security
S
Securelist
A
Arctic Wolf
MyScale Blog
MyScale Blog
H
Help Net Security
N
Netflix TechBlog - Medium
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
T
Threatpost
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Security Latest
Security Latest
T
Tor Project blog
V
Vulnerabilities – Threatpost
V
V2EX
AI
AI
Hugging Face - Blog
Hugging Face - Blog
大猫的无限游戏
大猫的无限游戏
博客园 - Franky
Simon Willison's Weblog
Simon Willison's Weblog
小众软件
小众软件
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
H
Hackread – Cybersecurity News, Data Breaches, AI and More
T
Troy Hunt's Blog
Schneier on Security
Schneier on Security
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
H
Heimdal Security Blog
Google Online Security Blog
Google Online Security Blog
Know Your Adversary
Know Your Adversary

逸思杂陈

家用网络 vlan 单线复用 Linux 平台 intel UHD 6xx 核显 openvino 探索 UI 区域检测的 vibe coding 复盘 mitmproxy 使用 esim 使用相关 阻止 bilibili 网页自动关注 Hexo 版本更新与技术债务 配置 Linux 作为主力操作系统 在 Linux 虚拟机中使用 PyAutoGUI 做自动化 语言的力量 联想笔记本 BIOS 跳过检测强制降级 redroid “设备未获得play保护机制认证” 问题 在 iOS 上访问安卓应用 在 VSCode 中用 Rust 刷LeetCode 跨域的那些事 HomeBrew 与无 root 权限 Linux 环境包管理 给 macOS 词典增加生词本功能 关闭子进程打开的文件描述符 容器内进程优雅退出 bash 语法备忘 MySQL 自定义数据库路径
Python 循环变量泄露与延迟绑定
Jay.Run · 2022-03-05 · via 逸思杂陈

循环变量泄露与延迟绑定叠加在一起,会产生一些让人迷惑的结果。

梦开始的地方

先看看一开始的问题,可以看到这里lambda函数的返回值一直在变。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
xx = []
for i in [1,2,3]:
xx.append(lambda: i)

print('a:', xx[0]())

for j in xx:
print(j())

print('b:', xx[0]())

for i in xx:
print(i, i())

print('c:', xx[0], xx[0]())

for i in [4, 5, 6]:
print(i)

print('d:', xx[0], xx[0]())

输出如下

1
2
3
4
5
6
7
8
9
10
11
12
13
a: 3
3
3
3
b: 3
<function main2.<locals>.<lambda> at 0x10ca30310> <function main2.<locals>.<lambda> at 0x10ca30310>
<function main2.<locals>.<lambda> at 0x10ca303a0> <function main2.<locals>.<lambda> at 0x10ca303a0>
<function main2.<locals>.<lambda> at 0x10ca30430> <function main2.<locals>.<lambda> at 0x10ca30430>
c: <function main2.<locals>.<lambda> at 0x10ca30310> <function main2.<locals>.<lambda> at 0x10ca30430>
4
5
6
d: <function main2.<locals>.<lambda> at 0x10ca30310> 6

循环变量泄露

由于Python没有块级作用域,所以循环会改变当前作用域变量的值,也就是循环变量泄露。
注意:Python3中列表推导式循环变量不会泄露,Python2中和常规循环一样泄露。

1
2
3
4
5
x = -1
for x in range(7):
if x == 6:
print(x, ': for x inside loop')
print(x, ': x in global')

输出如下

1
2
6 : for x inside loop
6 : x in global

闭包与延迟绑定

再讲一下闭包,在一个内部函数中,对外部作用域的变量进行引用,(并且一般外部函数的返回值为内部函数),那么内部函数就被认为是闭包。
这里所谓的引用可以也就是内部函数记住了变量的名称(而不是值,这个从ast语法树可以看出),而变量对应的值是会变化的。
如果在循环中定义闭包,引用的变量的值在循环结束才统一确定为最后一次循环时的值,也就是延迟绑定(lazy binding)。

所以下面的例子,xx的所有匿名函数的返回值均为3

1
2
3
xx = []
for i in [1,2,3]:
xx.append(lambda: i)

最后

再分析一开始的问题,这里的匿名函数引用了变量i,而i是全局变量,所以再次使用i作为循环变量时,列表中的匿名函数引用的值就被覆盖了。

正确做法:

  • 在独立的函数中定义闭包
  • 闭包引用的变量应该是其他函数不可修改的
  • 优先使用列表推导式

参考