生成器表达式评估有几个......用于... ...部分

时间:2016-09-17 22:14:44

标签: python python-3.x scope nested generator

问题:当Python看到这种表达时,它会做什么?

sum(sum(i) for j in arr for i in j)

我的想法上面的表达方式有效。但正如用Python's docs写的那样:

  

生成器表达式使用函数作用域

实现

不要冗长:)我有一个具有以下布局的数组(作为示例):

>>> arr = [
           [[1,2,3], [4,5,6]],
           [[7,8,9],[10,11,12]]
          ]

首先,我尝试将arr的所有元素与以下表达式相加:

>>> sum(sum(i) for i in j for j in arr)
NameError: name 'j' is not defined

它引发NameError,但为什么不UnboundLocalError: local variable 'j' referenced before assignment如果是使用函数范围实现的,for ... in ...从左到右或从右到右的评估规则是什么?剩下?什么是这个生成器表达式的等效生成器函数?

修改

我抓住了这个主意。感谢@vaultah的一些见解。在这种情况下,j是发送到生成器表达式的参数:

>>> sum(sum(i) for i in j for j in arr)  # NameError

这就是为什么我得到这个奇怪的NameError

@Eric answer显示了生成器表达式:

>>> sum(sum(i) for j in arr for i in j)

相当于:

>>> def __gen(arr):
        for j in arr:
            for i in j:
                yield sum(i)

>>> sum(__gen(arr))

2 个答案:

答案 0 :(得分:2)

  

当Python看到这种表达时,它会做什么?

container->inputOption

它变得相当于:

 sum(sum(i) for j in array for i in j)

请注意,def __gen(it): # "it" is actually in locals() as ".0" for j in it: for i in j: yield sum(i) sum(__gen(iter(arr))) 和名称解析都发生在函数范围之外。这仅适用于第一个__iter__循环

PEP 0289更详细地解释了这一点

答案 1 :(得分:1)

无论是生成器还是列表理解,理解嵌套都是一样的。通过列表理解更容易看到正在发生的事情,这就是我将在下面的示例中使用的内容。

假设:

>>> arr
[[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]]

您可以使用嵌套列表推导(或生成器)将Ints列表列表压平1级:

>>> [e for sl in arr for e in sl]
[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]

在给定结构的情况下,您可以通过再次嵌套完全展平(仅限示例;有更好的方法来展平深层嵌套列表):

>>> [e2 for sl2 in [e for sl in arr for e in sl] for e2 in sl2]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

由于sum采用可迭代,因此在您的示例中不需要第二次展平:

>>> [sum(e) for sl in arr for e in sl]
[6, 15, 24, 33]   # sum of those is 78...

理解的一般形式是:

[ expression      for a_variable in a_DEFINED_sequence     optional_predicate ]

通过使用未定义的名称,您可以获得与嵌套理解相同的NameError

>>> [c for c in not_defined]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'not_defined' is not defined

因此,您在sum(sum(i) for i in j for j in arr)上看到的错误是因为j尚未定义。理解从左到右,从内到外进行评估。 j作为序列的定义是其尝试使用的权利。

要将列表推导展开为嵌套循环,内部(或左手)部分将成为外部循环:

for sl in arr:
    for sl2 in sl:
        for e in sl2:
           # now you have each int in the LoLoInts...
           # you could use yield e for a generator here

您的最后一个问题:为什么TypeError带有gen = (j for j in arr)

那个生成器表达式什么都不做。例如:

>>> [j for j in arr]
[[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]]
>>> [j for j in arr] == arr
True

所以表达式(j for j in arr)只返回arr上的生成器。

并且sum不知道如何添加或者arr:

>>> sum(arr)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'list'

由于示例中的gen返回相同的数据结构,这是您的错误。

修复它:

>>> gen=(e for sl in arr for e in sl)
>>> sum(sum(li) for li in gen)
78