Python:对非空列表的迭代没有if语句却为空,为什么?
一个遍历非空序列的迭代器,如果没有过滤和聚合(比如 sum()
之类的),怎么会什么都不返回呢?
我们来看一个简单的例子:
sequence = ['a', 'b', 'c']
list((el, ord(el)) for el in sequence)
这个例子会返回 [('a', 97), ('b', 98), ('c', 99)]
,这是我们预期的结果。
现在,只需把 ord(el)
换成一个从某个生成器中取出第一个值的表达式,使用 (...).next()
— 请原谅这个有点牵强的例子:
def odd_integers_up_to_length(str):
return (x for x in xrange(len(str)) if x%2==1)
list((el, odd_integers_up_to_length(el).next()) for el in sequence)
这个例子会返回 []
。没错,返回的是空列表。没有 ('a',
其他内容 )
的元组。什么都没有。
但是我们并没有进行过滤、聚合或者减少操作。一个对 n
个对象的生成器表达式,如果没有过滤或聚合,应该会返回 n
个对象,对吧?这到底是怎么回事呢?
4 个回答
>>> seq=['a','b','c']
>>> list((el,4) for el in seq)
[('a',4), ('b',4), ('c',4)]
所以这里的问题不是出在list
上...
发生的事情是,当你调用 next()
时,会引发一个 StopIteration
异常,这个异常会向上冒泡到外部的生成器表达式,导致那个迭代停止。
StopIteration
是迭代器用来表示它已经完成的正常方式。一般情况下我们看不到这个异常,因为通常 next()
的调用是在一些会消耗迭代器的结构中,比如 for x in iterator
或 sum(iterator)
。但是当我们直接调用 next()
时,就需要自己处理这个 StopIteration
异常。如果不处理,就会在抽象层面出现问题,这里会导致外部迭代出现意想不到的行为。
我想教训就是:直接调用 next()
时要小心。
odd_integers_up_to_length(el).next()
这个代码会引发一个叫做 StopIteration 的错误,这个错误在这里没有被处理,但在它内部的生成器表达式中被捕获了,这样就会停止执行,而什么都不会返回。
看看第一次循环,当值是 'a' 的时候:
>>> odd_integers_up_to_length('a').next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration