python - 嵌套生成器表达式 - 意外结果

标签 python python-3.x nested generator generator-expression

测试代码如下:

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 = ... ) 形成一个迭代器,我希望最终结果能够反射(reflect) units 的最终分配值。和tens 。 OTOH,如果要在第 3 行评估该生成器表达式,生成结果元组,那么我期望 units 的第一个定义和tens待使用。

我看到的是MIX;即结果是 [31, 41, 32, 42] !?

谁能解释一下这种行为吗?

最佳答案

生成器表达式创建一个函数;一个只有一个参数,最外面的可迭代。

这里是单位,在创建生成器表达式时将其作为参数绑定(bind)到生成器表达式。

所有其他名称都是本地名称(例如 ab)、全局名称或闭包。 tens 作为全局变量进行查找,因此每次推进生成器时都会查找它。

因此,units 绑定(bind)到第 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 名称。

这记录在 original generators PEP 中:

Only the outermost for-expression is evaluated immediately, the other expressions are deferred until the generator is run:

g = (tgtexp  for var1 in exp1 if exp2 for var2 in exp3 if exp4)

is equivalent to:

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 :

Variables used in the generator expression are evaluated lazily when the __next__() method is called for generator object (in the same fashion as normal generators). However, the leftmost for clause is immediately evaluated, so that an error produced by it can be seen before any other possible error in the code that handles the generator expression. Subsequent for clauses cannot be evaluated immediately since they may depend on the previous for loop. For example: (x*y for x in range(10) for y in bar(x)).

PEP 有一个很好的部分来解释为什么名称(除了最外层的迭代)被延迟绑定(bind),请参阅 Early Binding vs. Late Binding .

关于python - 嵌套生成器表达式 - 意外结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22694321/

相关文章:

Python - 按分隔符出现次数拆分大字符串

python - 将字典值读取为正则表达式,返回匹配项

python - 找到目标后如何停止线程?

python - 将谷歌静态图像的像素转换为 LatLng 坐标

python - 使用 South (Mysql) 在 Django 中迁移问题

python - 嵌套在函数和属性查找中的类

mysql - 为什么我的嵌套光标显示两倍的结果?

python - Pandas 嵌套循环,其中一行与特定值匹配

python - 如何在excel表中创建具有相应值的新列

python - 如何向 3D Pandas DataFrame 添加嵌套列?