高级嵌套列表理解语法

ins*_*get 42 python syntax scope list-comprehension

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

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

(x for x in range(10) if x%2==0) # generates all even integers in range(10)
Run Code Online (Sandbox Code Playgroud)

我试图做的是编写一个生成两个生成器的生成器 - 第一个生成范围(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
Run Code Online (Sandbox Code Playgroud)

我不明白为什么'i'在转让之前被引用

我认为它可能与某些事情有关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
Run Code Online (Sandbox Code Playgroud)

这对我来说没有意义,所以我认为最好先尝试一些简单的事情.所以我回到列表并尝试:

>>> [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]
Run Code Online (Sandbox Code Playgroud)

我希望它与以下相同:

>>> 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?
Run Code Online (Sandbox Code Playgroud)

但是,当我试着预感时,这有效:

>>> [[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
Run Code Online (Sandbox Code Playgroud)

所以我认为这可能是一个问题,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]
Run Code Online (Sandbox Code Playgroud)

而现在我完全糊涂了.有人可以解释一下这种行为.我不明白为什么我的列表理解似乎不正确,也不理解if语句的范围是如何工作的.

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

Lie*_*yan 39

你需要使用一些括号:

((x for x in range(10) if x%2==i) for i in range(2))
Run Code Online (Sandbox Code Playgroud)

这对我来说没有意义,所以我认为最好先尝试一些简单的事情.所以我回到列表并尝试:

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

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

编辑:

循环的等价物:

(x for x in range(10) if x%2==i for i in range(2))
Run Code Online (Sandbox Code Playgroud)

将会:

l = []
for x in range(10):
    if x%2 == i:
        for i in range(2):
            l.append(x)
Run Code Online (Sandbox Code Playgroud)

这也给出了名称错误.

EDIT2:

括号版本:

((x for x in range(10) if x%2==i) for i in range(2))
Run Code Online (Sandbox Code Playgroud)

相当于:

li = []
for i in range(2):
    lx = []
    for x in range(10):
        if x%2==i:
            lx.append(x)
    li.append(lx)
Run Code Online (Sandbox Code Playgroud)


Nat*_*eld 8

Lie Ryan的for循环等价物引导我到以下,这看起来似乎工作得很好:

[x for i in range(2) for x in range(10) if i == x%2]
Run Code Online (Sandbox Code Playgroud)

输出

[0, 2, 4, 6, 8, 1, 3, 5, 7, 9]
Run Code Online (Sandbox Code Playgroud)


Dun*_*can 7

扩展Lie Ryan的答案:

something =(x表示范围内的x(10)如果x%2 == i表示i在范围(2)中)

相当于:

def _gen1():
    for x in range(10):
        if x%2 == i:
            for i in range(2):
                yield x
something = _gen1()
Run Code Online (Sandbox Code Playgroud)

而带括号的版本相当于:

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

    for i in range(2):
        yield _gen2()
something = _gen1()
Run Code Online (Sandbox Code Playgroud)

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

[<generator object <genexpr> at 0x02A0A968>, <generator object <genexpr> at 0x02A0A990>]
Run Code Online (Sandbox Code Playgroud)

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

>>> 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]
Run Code Online (Sandbox Code Playgroud)

我的建议是完全编写生成器函数:我认为尝试在i没有这样做的情况下获得正确的范围可能几乎是不可能的.


Gle*_*ard 5

谎言有句法问题的答案。建议:不要在发电机体内塞太多东西。函数更具可读性。

def make_generator(modulus):
    return (x for x in range(10) if x % 2 == modulus)
g = (make_generator(i) for i in range(2))
Run Code Online (Sandbox Code Playgroud)

  • 谢谢。我理解这一点,而且我确实更喜欢这样。但我试图对列表理解进行一些练习,看看我能把它们推到什么程度 (2认同)