这是测试代码:
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
的最终赋值。 OTOH,如果要在第3行评估该生成器表达式,产生结果元组,那么我期望使用units
和tens
的第一个定义。
我看到的是MIX;即,结果是[31, 41, 32, 42]
!?
任何人都可以解释这种行为吗?
答案 0 :(得分:3)
生成器表达式创建了各种各样的函数;一个只有一个参数,最外面的可迭代。
这里是units
,并且在创建生成器表达式时绑定为生成器表达式的参数。
所有其他名称都是本地(例如a
和b
),全局或闭包。 tens
被查找为全局,因此每次推进生成器时都会查找它。
因此,units
绑定到第3行的生成器,当您遍历最后一行的生成器表达式时,会查找tens
。
在将生成器编译为字节码并检查该字节码时,可以看到这一点:
>>> import dis
>>> genexp_bytecode = compile('(a + b for a in units for b in tens)', '<file>', 'single')
>>> dis.dis(genexp_bytecode)
1 0 LOAD_CONST 0 (<code object <genexpr> at 0x10f013ae0, file "<file>", line 1>)
3 LOAD_CONST 1 ('<genexpr>')
6 MAKE_FUNCTION 0
9 LOAD_NAME 0 (units)
12 GET_ITER
13 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
16 PRINT_EXPR
17 LOAD_CONST 2 (None)
20 RETURN_VALUE
MAKE_FUNCTION
字节码将生成器表达式代码对象转换为函数,并立即调用它,传入iter(units)
作为参数。此处未引用tens
名称。
只有最外面的for-expression被立即评估,另一个 表达式将延迟到生成器运行:
g = (tgtexp for var1 in exp1 if exp2 for var2 in exp3 if exp4)
相当于:
def __gen(bound_exp): for var1 in bound_exp: if exp2: for var2 in exp3: if exp4: yield tgtexp g = __gen(iter(exp1)) del __gen
和generator expressions reference:
当为生成器对象调用
__next__()
方法时(与普通生成器一样),懒惰地计算生成器表达式中使用的变量。但是,会立即评估最左边的for
子句,以便在处理生成器表达式的代码中的任何其他可能的错误之前可以看到它产生的错误。无法立即评估后续for
子句,因为它们可能取决于之前的for循环。例如:(x*y for x in range(10) for y in bar(x))
。
PEP有一个很好的部分,激励为什么名称(除了最外面的可迭代)被限制很晚,请参阅Early Binding vs. Late Binding。