生成器表达式创建各种函数;一个只有一个参数,最外面的可迭代。
这里是units
,并且在创建生成器表达式时将其绑定为生成器表达式的参数。
所有其他名称要么是局部变量(例如a
and 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
这里根本没有提到这个名字。
这记录在原始生成器 PEP中:
只有最外层的 for 表达式会立即计算,其他表达式会延迟到生成器运行:
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
并在生成器表达式参考中:
当为生成器对象调用该方法时,生成器表达式中使用的变量会被延迟计算__next__()
(以与普通生成器相同的方式)。但是,最左边的for
子句会立即被求值,因此可以在处理生成器表达式的代码中的任何其他可能错误之前看到由它产生的错误。for
不能立即评估后续子句,因为它们可能依赖于前一个 for 循环。例如:(x*y for x in range(10) for y in bar(x))
。
PEP 有一个很好的部分说明了为什么名称(除了最外层的可迭代)绑定较晚,请参阅Early Binding vs. Late Binding。