Python:高级嵌套列表理解语法

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

我本质上是试图写一个生成发电机的发电机。 使用列表理解的简单生成器将如下所示:

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

我试图做的是写一个生成器生成两个生成器 – 其中第一个生成范围(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 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 

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

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

我期望的是一样的:

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

但是,当我尝试了一下预感,这工作:

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

所以我认为这可能是一个问题,什么级别的范围的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] 

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

任何帮助将不胜感激

谢谢

PS:在certificate这个问题的同时,我意识到这看起来有点像一个家庭作业问题 – 事实并非如此。

你需要使用一些括号:

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

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

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

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

编辑:

等价的循环:

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

将会:

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

这也给名称错误。

EDIT2:

括号版本:

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

相当于:

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

Lie Rean的回答稍微扩展了一下:

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

相当于:

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

而括号版本相当于:

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

这实际上产生了两个发电机:

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

不幸的是,它产生的发电机有点不稳定,因为输出取决于你如何消耗它们:

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

我的build议是完整地写出生成器函数:我认为试图在没有这样做的情况下对i进行正确的作用域可能几乎是不可能的。

Lie Ryan的for循环等价物引导我进入下面,这似乎工作得很好:

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

输出

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

李对语法问题有答案。 一个build议:不要把太多东西塞进发电机的身体里。 一个函数更可读。

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