我正在使用列表推导来更好地理解它们,并且遇到了一些我无法解释的意外输出。我之前没有找到这个问题,但如果是/是/重复问题,我道歉。
我本质上是在尝试编写一个生成生成器的生成器。使用列表推导的简单生成器如下所示:
(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'在作业之前被引用
我认为它可能与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:在校对问题时,我意识到这看起来有点像家庭作业问题 - 事实并非如此。
答案 0 :(得分:39)
你需要使用一些括号:
((x for x in range(10) if x%2==i) for i in range(2))
这对我没有意义,所以我 认为最好尝试一下 先简单一点。所以我回到了名单 并试过:
[&GT;&GT;&GT; [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)
也会出现名称错误。
<强> 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)
答案 1 :(得分:8)
Lie Ryan的for-loop等价物引导我进行以下操作,看起来效果很好:
[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]
答案 2 :(得分: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()
而括号内的版本相当于:
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]
我的建议是完全编写生成器函数:我认为尝试在i
上进行正确的范围设定而不执行此操作可能几乎不可能。
答案 3 :(得分:3)
Lie有句法问题的答案。一个建议:不要在发电机的主体内充满东西。函数更具可读性。
def make_generator(modulus):
return (x for x in range(10) if x % 2 == modulus)
g = (make_generator(i) for i in range(2))