嵌套生成器表达式-意外结果 [英] Nested generator expression - unexpected result

查看:71
本文介绍了嵌套生成器表达式-意外结果的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是测试代码:

 单位= [1,2]十= [10,20]nums =(a + b代表a单位为b代表十个单位)单位= [3,4]十= [30,40][x表示x,以数字表示] 

在第3行的生成器表达式( nums = ... )形成迭代器的假设下,我希望最终结果能够反映 units 和 tens .OTOH,如果要在第3行对生成器表达式进行求值,生成结果元组,那么我希望使用 units tens 的第一个定义./p>

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

任何人都可以解释这种行为吗?

解决方案

生成器表达式创建各种 function ;一个只有一个参数,最外面的是可迭代的.

这里是 units ,并且在生成生成器表达式时绑定为生成器表达式的参数.

所有其他名称都是本地名称(例如 a b ),全局变量或闭包. tens 被视为一个全局变量,因此每次推进生成器时都会对其进行查找.

结果,在第3行将 units 绑定到生成器,在最后一行上遍历生成器表达式时,将查找 tens .

将生成器编译为字节码并检查该字节码时,您会看到以下信息:

 >>>导入dis>>>genexp_bytecode = compile('((a + b代表a以b为单位,以十为单位)','< file>','single')>>>dis.dis(genexp_bytecode)1 0 LOAD_CONST 0(0x10f013ae0处的<代码对象< genexpr> ;,文件< file>",第1行>)3 LOAD_CONST 1('< genexpr>')6 MAKE_FUNCTION 09 LOAD_NAME 0(单位)12 GET_ITER13 CALL_FUNCTION 1(1个位置,0个关键字对)16个PRINT_EXPR17 LOAD_CONST 2(无)20 RETURN_VALUE 

MAKE_FUNCTION 字节码将生成器表达式代码对象变成一个函数,然后立即调用它,并传入 iter(units)作为参数.此处完全未引用 tens 名称.

这已记录在原始生成器PEP 中:

仅最外面的for表达式立即被求值,另一个表达式推迟到生成器运行为止:

  g =(如果exp4为exp2,则exp1为var2,如果exp4为exp2,则为exp1中的var1的tgtexp) 

等效于:

  def __gen(bound_exp):对于bound_exp中的var1:如果exp2:对于exp3中的var2:如果exp4:产生tgtexpg = __gen(iter(exp1))del __gen 

,并在生成器表达式参考中:

当为生成器对象调用 __ next __()方法时(与普通生成器相同的方式),对生成器表达式中使用的变量进行延迟计算.但是,最左边的 for 子句会被立即求值,这样,在处理生成器表达式的代码中,它产生的错误就可以在任何其他可能的错误之前看到.随后的 for 子句可能无法立即求值,因为它们可能取决于先前的for循环.例如:(x * y表示范围内的x(10)表示bar(x)中的y).

PEP有一个很好的部分,它鼓励为什么名称(除了最外面的可迭代名称)绑定得晚,请参见 解决方案

A generator expression creates a function of sorts; one with just one argument, the outermost iterable.

Here that's units, and that is bound as an argument to the generator expression when the generator expression is created.

All other names are either locals (such as a and b), globals, or closures. tens is looked up as a global, so it is looked up each time you advance the generator.

As a result, units is bound to the generator on line 3, tens is looked up when you iterated over the generator expression on the last line.

You can see this when compiling the generator to bytecode and inspecting that bytecode:

>>> 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

The MAKE_FUNCTION bytecode turned the generator expression code object into a function, and it is called immediately, passing in iter(units) as the argument. The tens name is not referenced at all here.

This is documented in the 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

and in the 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)).

The PEP has an excellent section motivating why names (other than the outermost iterable) are bound late, see Early Binding vs. Late Binding.

这篇关于嵌套生成器表达式-意外结果的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆