Python:对非空列表的迭代没有if语句却为空,为什么?

5 投票
4 回答
778 浏览
提问于 2025-04-15 11:04

一个遍历非空序列的迭代器,如果没有过滤和聚合(比如 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 个回答

0
>>> seq=['a','b','c']
>>> list((el,4) for el in seq)
[('a',4), ('b',4), ('c',4)]

所以这里的问题不是出在list上...

4

发生的事情是,当你调用 next() 时,会引发一个 StopIteration 异常,这个异常会向上冒泡到外部的生成器表达式,导致那个迭代停止。

StopIteration 是迭代器用来表示它已经完成的正常方式。一般情况下我们看不到这个异常,因为通常 next() 的调用是在一些会消耗迭代器的结构中,比如 for x in iteratorsum(iterator)。但是当我们直接调用 next() 时,就需要自己处理这个 StopIteration 异常。如果不处理,就会在抽象层面出现问题,这里会导致外部迭代出现意想不到的行为。

我想教训就是:直接调用 next() 时要小心。

13

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

撰写回答