高级嵌套列表推导式语法
我在玩列表推导式,想更好地理解它们,但遇到了一些意想不到的输出,我搞不懂为什么会这样。我没有找到之前有人问过这个问题,如果这是个重复的问题,我在这里先道个歉。
我其实是想写一个生成器,这个生成器可以生成其他生成器。一个简单的使用列表推导式的生成器看起来是这样的:
(x for x in range(10) if x%2==0) # generates all even integers in range(10)
我想做的是写一个生成器,它能生成两个生成器——第一个生成器生成0到9之间的偶数,第二个生成器生成0到9之间的奇数。为此,我做了:
>>> (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'”的错误。
我以为这可能和 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
语句的作用范围是怎么回事。
附注:在校对这个问题时,我意识到这看起来有点像作业问题——但其实不是。
4 个回答
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]
你需要使用一些括号:
((x for x in range(10) if x%2==i) for i in range(2))
这让我有点困惑,所以我觉得最好先尝试一些简单的东西。于是我回到列表,试了试:
[>>> [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 中已经去掉了这种变量泄露的行为。
编辑:
下面这个等价的 for 循环:
(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)
这也会导致名称错误。
编辑2:
带括号的版本:
((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)