为什么Python中的迭代器会引发异常?


48

这是Java中迭代器的语法(C#中的语法有点相似):

Iterator it = sequence.iterator();

while (it.hasNext()) {
    System.out.println(it.next());
}

有道理。这是Python中的等效语法:

it = iter(sequence)
while True:
    try:
        value = it.next() 
    except StopIteration:
        break
    print(value)

我认为例外应该只在特殊情况下使用。

为什么Python使用异常来停止迭代?


Answers:


50

有一种非常Python化的方式来编写该表达式,而无需为a显式编写try-except块StopIteration

# some_iterable is some collection that can be iterated over
# e.g., a list, sequence, dict, set, itertools.combination(...)

for value in some_iterable:
    print(value)

如果您想了解为什么引入了为什么以及迭代器背后的逻辑,则可以阅读相关的PEP 234 255StopIteration

python的一般原则是要有一种做某事的方法(请参阅参考资料import this),最好是它的精美,显式,可读和简单的方式,这是pythonic方法可以满足的。仅当python没有为迭代器提供hasNext成员函数时,才需要使用等效代码。希望人们直接在迭代器中循环(如果您需要做其他事情以尝试读取它并捕获异常)。

StopIteration在迭代器末尾自动捕获异常是有意义的,并且EOFError如果您读取文件末尾,则类似于引发异常。


6
“ Pythonic”方式肯定更像是“按顺序获取价值:”而不是“按iter(序列)获取价值:” ..更新帖子?
Yam Marcovic

15
@Yam:我同意。取现有序列并将其转换为迭代器只是为了对其应用一个for循环不是pythonic;该序列已经是可迭代的,因此将a转换list为a listiterator是毫无意义的。我一直在第一线只遵循NullUserException的出发点,说明你应该如何遍历一个迭代器,这也是同样的道理,你应该遍历可迭代的(listsetstrtupledictfilegenerator,等)。我本可以做一些事情it = itertools.combinations("ABCDE", 2)来获得有意义的迭代器的更好示例。
jimbob博士2011年

1
it = iter(sequence)不需要。
Caridorc

2
@Caridorc-如果您阅读评论,那么您的观点得到了解决。并不需要遵循问题的起点(他们明确询问了iterators),您确实需要iter显式生成iterator(try type([])list)vs type(iter([]))listiterator))。
jimbob博士2015年

//,@drjimbob,您在此问题的第二条评论中提出了一个很好的观点。我对Iterables的高级功能有些陌生,如果不阅读评论,我不会发现。我认为,如果我们能看到关于问题本身如何可以更改为“ Python之道”作为答案的第一部分的话,这将有益于我们许多贫穷的自我教育者。
弥敦道(Nathan Basanese)'16

28

PEP 234中记录了python使用Exception停止迭代的原因:

有人质疑是否有信号通知迭代结束的异常代价是否太高。已经提出了StopIteration异常的几种替代方案:特殊值End表示结束,信号End()测试迭代器是否完成,甚至重用IndexError异常。

  • 特殊值的问题是,如果序列中包含该特殊值,则对该序列的循环将过早结束而不会发出任何警告。如果使用以N结尾的C字符串的经验没有教给我们这可能导致的问题,请想象一下,如果特殊的End值是一个内置的,则Python内省工具可能会遍历所有内置名称的列表带来的麻烦。名义上!

  • 调用end()函数每次迭代将需要两次调用。两次通话比一次通话加上一个异常测试要贵得多。特别是对时间要求严格的for循环可以非常便宜地测试一个异常。

  • 重用IndexError可能会引起混乱,因为它可能是真正的错误,但可能会由于过早结束循环而被掩盖。

注意:惯用的python遍历序列的方式如下:

for value in sequence:
    print (value)

20

这是哲学上的差异。Pythonic的设计理念是EAFP

寻求宽恕比允许容易。这种通用的Python编码风格假定有效键或属性的存在,并且在假定被证明为假的情况下捕获异常。这种干净快捷的样式的特点是存在许多tryexcept声明。该技术对比与LBYL普遍用于许多其它语言,如C风格。


7

只是Java实现具有一个hasNext()方法,因此您可以在执行之前检查空的迭代器next()。当您调用不next()带任何元素的Java迭代器时,将NoSuchElementException抛出a

如此有效,您可以像在try..except中一样在Java中进行try..catch。是的,按照先前的回答,哲学在Pythonic世界中非常重要。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.