Python 高级嵌套列表理解语法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3766711/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 12:39:33  来源:igfitidea点击:

Advanced Nested List Comprehension Syntax

pythonsyntaxscopelist-comprehension

提问by inspectorG4dget

I was playing around with list comprehensions to get a better understanding of them and I ran into some unexpected output that I am not able to explain. I haven't found this question asked before, but if it /is/ a repeat question, I apologize.

我在玩列表推导式以更好地理解它们,但遇到了一些我无法解释的意外输出。我之前没有发现这个问题,但如果它/是/一个重复的问题,我很抱歉。

I was essentially trying to write a generator which generated generators. A simple generator that uses list comprehension would look like this:

我本质上是在尝试编写一个生成生成器的生成器。使用列表推导式的简单生成器如下所示:

(x for x in range(10) if x%2==0) # generates all even integers in range(10)

What I was trying to do was write a generator that generated two generators - the first of which generated the even numbers in range(10) and the second of which generated the odd numbers in range(10). For this, I did:

我试图做的是编写一个生成器来生成两个生成器 - 第一个生成范围(10)中的偶数,第二个生成范围(10)中的奇数。为此,我做了:

>>> (x for x in range(10) if x%2==i for i in range(2))
<generator object <genexpr> at 0x7f6b90948f00>

>>> for i in g.next(): print i
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <genexpr>
UnboundLocalError: local variable 'i' referenced before assignment
>>> g.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> g = (x for x in range(10) if x%2==i for i in range(2))
>>> g
<generator object <genexpr> at 0x7f6b90969730>
>>> g.next()
Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 1, in <genexpr>
    UnboundLocalError: local variable 'i' referenced before assignment

I don't understand why 'i' is being referenced before assignment

我不明白为什么在赋值之前引用了“i”

I thought it might have had something to do with i in range(2), so I did:

我认为这可能与 有关i in range(2),所以我做了:

>>> g = (x for x in range(10) if x%2==i for i in [0.1])
>>> g
<generator object <genexpr> at 0x7f6b90948f00>
>>> g.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <genexpr>
UnboundLocalError: local variable 'i' referenced before assignment

This didn't make sense to me, so I thought it best to try something simpler first. So I went back to lists and tried:

这对我来说没有意义,所以我认为最好先尝试更简单的东西。所以我回到列表并尝试:

>>> [x for x in range(10) if x%2==i for i in range(2)]
[1, 1, 3, 3, 5, 5, 7, 7, 9, 9]

which I expected to be the same as:

我期望与以下相同:

>>> l = []
>>> for i in range(2):
...     for x in range(10):
...             if x%2==i:
...                     l.append(x)
... 
>>> l
[0, 2, 4, 6, 8, 1, 3, 5, 7, 9] # so where is my list comprehension malformed?

But when I tried it on a hunch, this worked:

但是当我凭直觉尝试时,这奏效了:

>>> [[x for x in range(10) if x%2==i] for i in range(2)]
[[0, 2, 4, 6, 8], [1, 3, 5, 7, 9]] # so nested lists in nested list comprehension somehow affect the scope of if statements? :S

So I thought it might be a problem with what level of scope the ifstatement operates in. So I tried this:

所以我认为这可能是if语句操作的范围级别的问题。所以我尝试了这个:

>>> [x for x in range(10) for i in range(2) if x%2==i]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

And now I'm thoroughly confused. Can someone please explain this behavior. I don't understand why my list comprehensions seem to be malformed, nor do I understand how the scoping of the ifstatements work.

现在我彻底糊涂了。有人可以解释这种行为。我不明白为什么我的列表理解似乎格式错误,我也不明白if语句的范围是如何工作的。

PS: While proof-reading the question, I realized that this does look a bit like a homework question - it is not.

PS:在校对问题时,我意识到这确实有点像家庭作业问题 - 事实并非如此。

采纳答案by Lie Ryan

you need to use some parentheses:

你需要使用一些括号:

((x for x in range(10) if x%2==i) for i in range(2))

This didn't make sense to me, so I thought it best to try something simpler first. So I went back to lists and tried:

[>>> [x for x in range(10) if x%2==i for i in range(2)] [1, 1, 3, 3, 5, 5, 7, 7, 9, 9]

这对我来说没有意义,所以我认为最好先尝试更简单的东西。所以我回到列表并尝试:

[>>> [x for x in range(10) if x%2==i for i in range(2)] [1, 1, 3, 3, 5, 5, 7, 7, 9, 9]

That worked because a previous list comprehension leaks the i variable to the enclosing scope, and become the i for the current one. Try starting a fresh python interpreter, and that would fail due to NameError. The counter's leaking behavior has been removed in Python 3.

这是有效的,因为之前的列表理解将 i 变量泄漏到封闭范围内,并成为当前范围的 i 。尝试启动一个新的 python 解释器,但由于 NameError 会失败。计数器的泄漏行为已在 Python 3 中删除。

EDIT:

编辑:

The equivalent for loop for:

等效的 for 循环:

(x for x in range(10) if x%2==i for i in range(2))

would be:

将是:

l = []
for x in range(10):
    if x%2 == i:
        for i in range(2):
            l.append(x)

which also gives a name error.

这也给出了一个名称错误。

EDIT2:

编辑2:

the parenthesed version:

括号中的版本:

((x for x in range(10) if x%2==i) for i in range(2))

is equivalent to:

相当于:

li = []
for i in range(2):
    lx = []
    for x in range(10):
        if x%2==i:
            lx.append(x)
    li.append(lx)

回答by Glenn Maynard

Lie has the answer to the syntactical question. A suggestion: don't stuff so much into the body of a generator. A function is much more readable.

谎言有句法问题的答案。一个建议:不要在生成器的主体中塞入太多东西。函数更具可读性。

def make_generator(modulus):
    return (x for x in range(10) if x % 2 == modulus)
g = (make_generator(i) for i in range(2))

回答by Duncan

Expanding on Lie Ryan's answer a bit:

稍微扩展一下 Lie Ryan 的回答:

something = (x for x in range(10) if x%2==i for i in range(2))

something = (x for x in range(10) if x%2==i for i in range(2))

is equivalent to:

相当于:

def _gen1():
    for x in range(10):
        if x%2 == i:
            for i in range(2):
                yield x
something = _gen1()

whereas the parenthesised version is equivalent to:

而括号中的版本相当于:

def _gen1():
    def _gen2():
        for x in range(10):
            if x%2 == i:
                yield x

    for i in range(2):
        yield _gen2()
something = _gen1()

This does actually yield the two generators:

这实际上产生了两个生成器:

[<generator object <genexpr> at 0x02A0A968>, <generator object <genexpr> at 0x02A0A990>]

Unfortunately the generators it yields are somewhat unstable as the output will depend on how you consume them:

不幸的是,它产生的生成器有些不稳定,因为输出将取决于您如何使用它们:

>>> gens = ((x for x in range(10) if x%2==i) for i in range(2))
>>> for g in gens:
        print(list(g))

[0, 2, 4, 6, 8]
[1, 3, 5, 7, 9]
>>> gens = ((x for x in range(10) if x%2==i) for i in range(2))
>>> for g in list(gens):
        print(list(g))

[1, 3, 5, 7, 9]
[1, 3, 5, 7, 9]

My advice is to write the generator functions out in full: I think trying to get the correct scoping on iwithout doing that may be all but impossible.

我的建议是完整地写出生成器函数:我认为试图在i不这样做的情况下获得正确的范围可能几乎是不可能的。

回答by Nathan Kronenfeld

Lie Ryan's for-loop equivalent leads me to the following, which does seem to work just fine:

Lie Ryan 的 for-loop 等价物使我想到以下内容,这似乎工作得很好:

[x for i in range(2) for x in range(10) if i == x%2]

outputs

产出

[0, 2, 4, 6, 8, 1, 3, 5, 7, 9]