大家先看两个两个例子,比如使用filter过滤出偶数
l = [1,2,3]
lf1 = filter(lambda x: x%2==0, l)
l = [4,5,6]
lf2 = filter(lambda x: x%2==0, l)
print(list(lf1)) # 结果 >>> [2]
print(list(lf2)) # 结果 >>> [4, 6]
比如计算平方
l = [1,2,3]
lf1 = (i**2 for i in l)
l = [4,5,6]
lf2 = (i**2 for i in l)
print(list(lf1)) # 结果 >>> [1, 4, 9]
print(list(lf2)) # 结果 >>> [16, 25, 36]
重点来了,如果我用这个切片,取出前两个元素,大家觉得结果是什么?
l = 'ABC'
l_f1 = (l[i] for i in range(2))
l = 'DEF'
l_f2 = (l[i] for i in range(2))
print(list(l_f1))
print(list(l_f2))
居然都是
['D', 'E']
['D', 'E']
这种情况下,传递给生成器(...)的变量 l 居然不是内存地址,而是一个符号。这太奇怪了吧,想不通。
|      1siluni      2022-10-20 13:46:06 +08:00  3 应该不是传了符号,而是 generator 本身是 lazy 的,在调用 list(l_f1)的时候才真正执行了运算,这时 l 已经指向第二个值了 | 
|  |      2lambdaq      2022-10-20 13:47:48 +08:00  1 l_f1 =   和 l_f2 = 后面如果是「小括号」是惰性求值的,你不 print 它就不计算。改成「方括号」就没问题了。 基础姿势。 | 
|      3stein42      2022-10-20 13:48:25 +08:00  1 生成器里面第一个表达式是延迟求值的。 相当于: map(lambda i: l[i], range(2)) 每次计算会去读 l 的值。 函数式编程需要避免赋值这些有副作用的操作。 | 
|      4TongDu OP @siluni 我最开始也是那么理解的,但是前两个例子可以排除这样的情况。我想到了给函数传参,传入的应该就是内存地址。对于`(y[i] for i in x)`这样的生成器,x 应该是按照函数外传参进去的(第一第二个例子可佐证),而 y 不是传参进去的,而是属于在局部 local 域里面直接获取外部的变量。 | 
|      5deplivesb      2022-10-20 13:56:04 +08:00  3 | 
|  |      6clino      2022-10-20 13:56:25 +08:00 你先入为主了,这两种选择明显是取变量比取地址更自然 | 
|      7siluni      2022-10-20 13:56:42 +08:00 @TongDu 前两个例子是 iterable 的循环,但 range()生成的是 generator 。iterable 的值是一直在内存里的,generator 是调用 next()的时候才会计算的 | 
|      8apake      2022-10-20 14:17:08 +08:00  1 并不是 惰性求值的问题,   两个例子都是 惰性求值.    楼主的意思是  generator 的执行环境 并不像 函数那样 封装了一个 闭包环境. | 
|  |      9whoami9894      2022-10-20 14:17:23 +08:00  7 (i**2 for i in l): l 是创建生成器时闭包捕获的,i ** 2 是 f.next() 时计算的 *** (l[i] for i in range(2)): 闭包没有捕获任何变量,计算 l[i] 时从外部作用域获取 l *** 两个表达式对应的 opcode ,注意看 LOAD opcode ``` 2 8 LOAD_NAME 1 (list) 10 LOAD_CONST 1 (<code object <genexpr> at 0x10256d3a0, file "<dis>", line 2>) 12 LOAD_CONST 2 ('<genexpr>') 14 MAKE_FUNCTION 0 16 LOAD_NAME 0 (l) 18 GET_ITER 20 CALL_FUNCTION 1 22 CALL_FUNCTION 1 24 POP_TOP 3 26 LOAD_NAME 1 (list) 28 LOAD_CONST 3 (<code object <genexpr> at 0x10256d450, file "<dis>", line 3>) 30 LOAD_CONST 2 ('<genexpr>') 32 MAKE_FUNCTION 0 34 LOAD_NAME 2 (range) 36 LOAD_CONST 4 (2) 38 CALL_FUNCTION 1 40 GET_ITER 42 CALL_FUNCTION 1 44 CALL_FUNCTION 1 46 POP_TOP 48 LOAD_CONST 5 (None) 50 RETURN_VALUE Disassembly of <code object <genexpr> at 0x10256d3a0, file "<dis>", line 2>: 2 0 LOAD_FAST 0 (.0) >> 2 FOR_ITER 14 (to 18) 4 STORE_FAST 1 (i) 6 LOAD_FAST 1 (i) 8 LOAD_CONST 0 (2) 10 BINARY_POWER 12 YIELD_VALUE 14 POP_TOP 16 JUMP_ABSOLUTE 2 >> 18 LOAD_CONST 1 (None) 20 RETURN_VALUE Disassembly of <code object <genexpr> at 0x10256d450, file "<dis>", line 3>: 3 0 LOAD_FAST 0 (.0) >> 2 FOR_ITER 14 (to 18) 4 STORE_FAST 1 (i) 6 LOAD_GLOBAL 0 (l) 8 LOAD_FAST 1 (i) 10 BINARY_SUBSCR 12 YIELD_VALUE 14 POP_TOP 16 JUMP_ABSOLUTE 2 >> 18 LOAD_CONST 0 (None) 20 RETURN_VALUE ``` | 
|      10MRlaopeng      2022-10-20 14:25:32 +08:00 因为 range(2) 生成的 [0,1] | 
|  |      11amlee      2022-10-20 15:18:23 +08:00  5 生成器表达式中的 for 子句是立即计算的,这时候有闭包环境。 但是生成器表达式的小括号一开头的那个表达式是惰性计算的。 #这时候闭包的 i 变量存的是 l 中的值,表达式 i**2 是惰性计算的,放入 list()的时候才计算 lf1 = (i**2 for i in l) #这时候闭包的 i 变量存的是 [0, 1] 中的值,表达式 l[i] 是惰性计算的,放入 list()的时候才计算 lf2 = (l[i] for i in range(2)) 所以,在执行 print(list(lf2)) 之前,l 被重新赋值了,就会出现 op 说的情况。 这就是函数式编程为什么强调 immutable | 
|  |      12westoy      2022-10-20 15:32:56 +08:00 >>> l = 'ABC' >>> l_f1 = (lambda l=l: (l[i] for i in range(2)))() >>> l = 'DEF' >>> l_f2 = (lambda l=l: (l[i] for i in range(2)))() >>> print(list(l_f2)) ['D', 'E'] >>> print(list(l_f1)) ['A', 'B'] | 
|  |      15milkpuff      2022-10-20 18:28:30 +08:00 >>> (c[i] for i in range(5)) <generator object <genexpr> at 0x000002A859CD4820> >>> c Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'c' is not defined 当没有变量 c 时,也能正确创建 generator | 
|      16nyxsonsleep      2022-10-20 22:02:57 +08:00  1 @crab 生成器特性 | 
|      17jurassic2long      2022-10-21 10:50:24 +08:00 这重点不在生成器,而是在: i for i in l 与 l[i] for i in range(3) 的区别 | 
|  |      18brucmao      2022-10-21 11:22:16 +08:00 @westoy 看不明白 lambda l=l ,这里默认参数为啥这样传 ``` l = "ABC" l_f1 = (lambda l: (l[i] for i in range(2)))(l) l = "DEF" l_f2 = (lambda l: (l[i] for i in range(2)))(l) print(list(l_f2)) print(list(l_f1)) ``` | 
|      20zlstone      2022-10-21 12:03:32 +08:00 python 不存在取地址,底层一直传的就是 c struct 本身,只是因为 struct 里面存的是一个地址,所以看起来像是取地址的行为。 第一个示例传的就是两个不同的 list ,所以 filter 出来的东西是不一样的,如果这么写,这两个结果就是相同的 ``` l = [1,2,3] lf1 = filter(lambda x: x%2==0, l) l.append(4) lf2 = filter(lambda x: x%2==0, l) print(list(lf1)) # 结果 >>> [2, 4] print(list(lf2)) # 结果 >>> [2, 4] ``` |