以下是测试代码:
units = [1, 2] tens = [10, 20] nums = (a + b for a in units for b in tens) units = [3, 4] tens = [30, 40] [x for x in nums]
假设第 3 行 ( nums = ...) 上的生成器表达式形成一个迭代器,我期望最终结果能够反映units和的最终分配值tens。另一方面,如果要在第 3 行评估该生成器表达式,并生成结果元组,那么我希望使用units和的第一个定义。tens
nums = ...
units
tens
我看到的是 MIX;也就是说,结果是[31, 41, 32, 42]!?
[31, 41, 32, 42]
有人能解释这种行为吗?
这种行为与 Python 生成器表达式的惰性求值和作用域绑定方式有关。以下是对代码行为的逐步解释:
units = [1, 2] tens = [10, 20] nums = (a + b for a in units for b in tens) # 创建生成器表达式 units = [3, 4] # 修改 units tens = [30, 40] # 修改 tens [x for x in nums] # 消耗生成器
a
b
也就是说,当你写 nums = (a + b for a in units for b in tens) 时,并不会立即计算 a + b。相反,units 和 tens 的引用被捕获,而不是它们的当前值。
nums = (a + b for a in units for b in tens)
a + b
惰性求值:
当你调用 [x for x in nums] 时,才会开始逐一迭代 nums 中的每个值。
[x for x in nums]
nums
修改后的 units 和 tens 生效:
因此,生成器表达式中的 for a in units 和 for b in tens 实际上会使用 units = [3, 4] 和 tens = [30, 40]。
for a in units
for b in tens
units = [3, 4]
tens = [30, 40]
混合结果:
[3, 4]
[30, 40]
因此,结果是: - 3 + 30 = 31 - 3 + 40 = 41 - 4 + 30 = 32 - 4 + 40 = 42
3 + 30 = 31
3 + 40 = 41
4 + 30 = 32
4 + 40 = 42
最终输出 [31, 41, 32, 42]。
如果你希望生成器表达式绑定 units 和 tens 的初始值,而不受后续修改影响,可以在定义生成器表达式时立即复制 units 和 tens:
units = [1, 2] tens = [10, 20] nums = (a + b for a in list(units) for b in list(tens)) # 创建生成器时复制值 units = [3, 4] tens = [30, 40] print([x for x in nums]) # 输出 [11, 21, 12, 22]
或者,将生成器表达式立即转换为列表,以强制计算所有值:
units = [1, 2] tens = [10, 20] nums = [(a + b) for a in units for b in tens] # 列表推导强制计算 units = [3, 4] tens = [30, 40] print(nums) # 输出 [11, 21, 12, 22]
生成器表达式的惰性求值和作用域绑定机制会导致它在被迭代时使用变量的当前值。这种行为有时可能导致混淆,但理解其原理后可以通过复制变量值或强制计算来避免问题。