求助,分开写输出是[12, 13, 14, 15];用 for 输出是[20, 21, 22, 23],请问为啥呢

2018-10-09 15:58:29 +08:00
 ropon
def add(a, b):
return a + b

def test():
for r_i in range(4):
yield r_i

g = test()
#0 1 2 3

# g = (add(2, i) for i in g)
# g = (add(10, i) for i in g)
# print(list(g))
#[12, 13, 14, 15]

for n in [2, 10]:
# 0 1 2 3
# 2 1 2 3
g = (add(n, i) for i in g)
#第一次循环 n=2,i 第一次循环 0 相加结果 2 3 4 5
#第二次循环 n=10,i 第一次循环 2 相加结果 12 13 14 15
print(list(g))
#为啥输出[20, 21, 22, 23]
3492 次点击
所在节点    Python
20 条回复
xyxc0673
2018-10-09 16:21:37 +08:00
打印 add 的参数 a 和 b,可以发现 a 在循环的方式中始终是 10,也就是 n 引用的始终是最后一次循环 n 的值,这和直接传值的方式是不一样的。
xyxc0673
2018-10-09 16:33:04 +08:00
@xyxc0673 #1 如果用列表生成式就不会出现这种情况,因为生成器只有在被遍历时才会进行运算。
xyxc0673
2018-10-09 16:33:57 +08:00
@xyxc0673 #2 列表生成式则会一次性进行所有的运算
ropon
2018-10-09 16:43:10 +08:00
换成列表生产式 g = [add(n, i) for i in g],for 和单独传值结果一致[12, 13, 14, 15],不明白是怎么推算出[20, 21, 22, 23],for 第二次循环会覆盖上次循环的值吧。
ballshapesdsd
2018-10-09 16:44:40 +08:00
感觉是 python 语言的糟粕。。
ropon
2018-10-09 17:01:08 +08:00
如果遍历一次生成器表达式,那么 g 就没值了,生成器表达式 g = (add(n, i) for i in g) for i in g 不会遍历 g 么
xyxc0673
2018-10-09 17:07:31 +08:00
@ropon #6 g 在第一次遍历时没值了,但是你又赋值给 g 了。。。
ropon
2018-10-09 17:18:21 +08:00
@xyxc0673 逐级遍历吧
比如一开始 g 是 0 1 2 3,第一次循环 的第一次遍历
2 1 2 3
2 3 2 3
2 3 4 3
2 3 4 5

这样理解对么
goofool
2018-10-09 17:21:36 +08:00
n 一直是 10 哇,结果应该是 10+10+i
ropon
2018-10-09 17:25:13 +08:00
@goofool 10+10+i 是怎么来的呢
xyxc0673
2018-10-09 17:25:33 +08:00
@ropon #8 不懂你在讲哪个方面了。。。
在循环的方式下,第一次循环时,g 的值为
(
add(2, 0),
add(2, 1),
add(2, 2),
add(2, 3)
)
第二次循环时,g 的值为
(
add(10, add(10, 0)),
add(10, add(10, 1)),
add(10, add(10, 2)),
add(10, add(10, 3))
)
ropon
2018-10-09 17:30:25 +08:00
@xyxc0673 原来如此,明白了,感谢
goofool
2018-10-09 17:34:08 +08:00
@ropon 我记得叫 late binding 机制吧,两次计算的时候 n=10
goofool
2018-10-09 17:53:14 +08:00
@goofool 你把 2 改成 None 或者字符串,你就发现问题了
savebox
2018-10-09 17:56:55 +08:00
g = (add(n, i) for i in g) 改成 g = [add(n, i) for i in g] 就达到要求了
ropon
2018-10-09 20:21:39 +08:00
@xyxc0673 整理如下,看理解对吗?

#总结:推导式有,列表推导式、字典推导式、集合推导式,没有元组推导式
#生成器表达式:(结果 for 变量 in 可迭代对象 if 条件) 生成器表达式可直接获取其对象,对象可直接使用 for 循环,生成器具有惰性机制

def add(a, b):
return a + b

def test():
for r_i in range(4):
yield r_i

g = test()
#0 1 2 3

# g = (add(2, i) for i in g)

#g 的值是
# (
# add(2, 0),
# add(2, 1),
# add(2, 2),
# add(2, 3)
# )

# g = (add(10, i) for i in g)

#g 的值是
# (
# add(10, add(2, 0)),
# add(10, add(2, 1)),
# add(10, add(2, 2)),
# add(10, add(2, 3))
# )
#遍历生成器开始运算输出结果
# print(list(g))
#输出[12, 13, 14, 15]

for n in [2, 10]:
# g = [add(n, i) for i in g] #列表生成式会一次性进行所有的运算
# 第一次循环 n=2,i 第一次循环 0 相加结果 2 3 4 5
# 第二次循环 n=10,i 第一次循环 2 相加结果 12 13 14 15

g = (add(n, i) for i in g) # 生成器表达式只有在被遍历时才会进行运算
#第一次循环 n=2,i 从 0 1 2 3 循环,因生成器具有惰性机制,n 并没有取对应值,只是指向对应内存地址,g 的值是
# (
# add(n, 0),
# add(n, 1),
# add(n, 2),
# add(n, 3)
# )
#第二次循环 n=10,i 从 add(2, 0) add(2, 1) add(2, 2) add(2, 3)循环,同理,g 的值是
# (
# add(n, add(n, 0)),
# add(n, add(n, 1)),
# add(n, add(n, 2)),
# add(n, add(n, 3))
# )
#for 循环完,系统会释放 n,释放之前 n 先取值,g 的值是
# (
# add(10, add(10, 0)),
# add(10, add(10, 1)),
# add(10, add(10, 2)),
# add(10, add(10, 3))
# )
#遍历生成器开始运算输出结果
print(list(g))
#输出[20, 21, 22, 23]
ropon
2018-10-09 20:57:36 +08:00
a = [1, 2]
a[1] = a
print(a[1])
补门一个问题
为啥结果是:
[1, [...]]
ruoxin123
2018-10-09 22:03:07 +08:00
@ropon
因为 a[1]是 a 自己啊
所以会无限循环下去
你会发现 a[1], a[1][1], a[1][1][1]... 都是[1, [...]]
xyxc0673
2018-10-09 22:32:36 +08:00
@ropon #16

@goofool #13 说的准确一些,因为 lambda 表达式放在列表生成式也会出现这种现象,具体的讲解可以搜索关键词 late binding
ropon
2018-10-09 23:13:48 +08:00
@ruoxin123
a[1]=a
第一次 a=[1, [1, 2] ] 第二次 a=[1, [1, [1, 2] ] ] 第三次 a=[1, [1, [1, [1, 2] ] ] ]
这样理解对吗

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/496001

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX