在Python中,iterable的接口是iterator interface的子集。这具有的优点是,在许多情况下,它们可以以相同的方式处理。但是,两者之间存在重要的语义差异,因为对于可迭代的__iter__
,返回一个新的迭代器对象而不仅仅是self
。我怎样才能测试一个iterable真的是一个可迭代的而不是一个迭代器?从概念上讲,我理解iterables是集合,而迭代器只管理迭代(即跟踪位置),但不是集合本身。
例如,当想要多次循环时,差异很重要。如果给出了迭代器,那么第二个循环将不起作用,因为迭代器已经用完并直接引发StopIteration
。
测试next
方法很有诱惑力,但这看起来很危险,而且有些不对。我应该检查第二个循环是否为空?
有没有办法以更加pythonic的方式进行这样的测试?我知道这听起来像是针对EAFP的LBYL的经典案例,所以也许我应该放弃?或者我错过了什么?
修改 S.Lott在下面的回答中说,这主要是一个想要在迭代器上进行多次传递的问题,并且首先不应该这样做。但是,在我的情况下,数据非常大,并且根据情况必须多次传递以进行数据处理(绝对没有办法解决这个问题)。
迭代也由用户提供,并且对于单次传递足够的情况,它将与迭代器一起工作(例如,为了简单起见,由生成器创建)。但是,如果用户在需要多次传递时只提供迭代器,那么防止这种情况会很好。
编辑2:
实际上这是Abstract Base Classes的一个非常好的例子。迭代器和迭代中的__iter__
方法具有相同的名称,但是在语义上是不同的!因此hasattr
无用,但isinstance
提供了一个干净的解决方案。
答案 0 :(得分:13)
'iterator' if obj is iter(obj) else 'iterable'
答案 1 :(得分:4)
然而,两者之间存在重要的语义差异......
不是真正的语义或重要。它们都是可迭代的 - 它们都使用for语句。
例如,当想要多次循环时,差异很重要。
什么时候出现?你必须更加具体。在极少数情况下,当您需要通过可迭代集合进行两次传递时,通常会有更好的算法。
例如,假设您正在处理列表。您可以根据需要迭代列表。你为什么纠缠于迭代器而不是迭代?好吧那没用。
好的,这是一个。您正在两遍中读取文件,并且您需要知道如何重置可迭代文件。在这种情况下,它是一个文件,并且seek
是必需的;或者关闭并重新开放。感觉很蠢。你可以readlines
获得一个允许两次通过而没有复杂性的列表。所以这不是必要的。
等等,如果我们有一个如此大的文件,我们无法将其全部读入内存怎么办?并且,由于不明原因,我们也无法寻求。那么呢?
现在,我们已经完成了两次传球的细节。在第一关,我们积累了一些东西。索引或摘要或其他内容。索引包含所有文件的数据。总结通常是对数据进行重组。通过从“摘要”到“重组”的小改动,我们在新结构中保留了文件的数据。在这两种情况下,我们都不需要文件 - 我们可以使用索引或摘要。
所有“两次通过”算法都可以更改为原始迭代器的一次传递或可迭代,以及另一种不同数据结构的传递。
这既不是LYBL也不是EAFP。这是算法设计。您不需要重置迭代器 - YAGNI。
修改强>
以下是迭代器/可迭代问题的示例。它只是一个设计不佳的算法。
it = iter(xrange(3))
for i in it: print i,; #prints 1,2,3
for i in it: print i,; #prints nothing
这很简单。
it = range(3)
for i in it: print i
for i in it: print i
“多次并行”是平凡的。编写需要可迭代的API。当有人拒绝阅读API文档或拒绝阅读它后,他们的东西就会破坏。应该如此。
“很好地防范案例是用户在需要多次传递时只提供迭代器”都是疯狂的人编写破坏我们简单API的代码的例子。
如果有人疯狂阅读大多数(但不是全部API文档)并在需要需要的时候提供迭代器,那么你需要找到这个人并教他们(1)如何阅读所有API文档和(2)遵循API文档。
“保障”问题不太现实。这些疯狂的程序员非常罕见。在少数情况下,你知道他们是谁并可以帮助他们。
修改2
“我们必须多次读取相同的结构”算法是一个基本问题。
不要这样做。
for element in someBigIterable:
function1( element )
for element in someBigIterable:
function2( element )
...
相反,请这样做。
for element in someBigIterable:
function1( element )
function2( element )
...
或者,考虑这样的事情。
for element in someBigIterable:
for f in ( function1, function2, function3, ... ):
f( element )
在大多数情况下,算法的这种“枢轴”导致程序可能更容易优化,并且可能是性能的净改进。
答案 2 :(得分:2)
import itertools
def process(iterable):
work_iter, backup_iter= itertools.tee(iterable)
for item in work_iter:
# bla bla
if need_to_startover():
for another_item in backup_iter:
雷蒙德从Guido那里借来的那台该死的时间机器......
答案 3 :(得分:0)
由于Python的鸭子打字,
如果任何对象定义next()
并且__iter__()
方法返回自身,则该对象是可迭代的。
如果对象本身没有next()
方法,__iter__()
可以返回任何具有next()
方法的对象
您可以参考此问题查看Iterability in Python