如何检查迭代器是否实际上是一个迭代器容器?

6

以下是一个迭代器容器的虚拟示例(真实的示例读取的是一个太大以至于无法放入内存的文件):

class DummyIterator:
    def __init__(self, max_value):
        self.max_value = max_value

    def __iter__(self):
        for i in range(self.max_value):
            yield i

def regular_dummy_iterator(max_value):
    for i in range(max_value):
        yield i

这使我能够多次迭代值,以便我可以实现类似以下的内容:
def normalise(data):
    total = sum(i for i in data)
    for val in data:
        yield val / total

# this works when I call next()
normalise(DummyIterator(100))

# this doesn't work when I call next()
normalise(regular_dummy_iterator(100))

如何在normalise函数中检查传递给我一个迭代器容器而不是普通的生成器?


你不必一次性将文件读入内存,那你为什么需要这个? - Padraic Cunningham
1
如果您需要对大文件执行多次遍历,只需使用其.seek()方法将其倒回即可,就像Padraic所说的那样。 - PM 2Ring
1
你能否编辑代码以使其可运行?在 __init__ 中添加 self 参数,并在 __iter__ 中使用 self.max_value - 301_Moved_Permanently
2个回答

9
首先:不存在所谓的“迭代器容器”。你有一个可迭代对象。
可迭代对象会产生一个迭代器。任何迭代器也都是可迭代对象,但会产生它自己作为迭代器:
>>> list_iter = iter([])
>>> iter(list_iter) is list_iter
True

如果 iter(ob) is ob 的测试结果为false,则表示您没有迭代器。


3

您可以使用collections.abc模块来测试是否具有迭代器(被消耗一次next引发StopIteration异常)与仅具有可迭代性(可能可以多次迭代)。以下是一个示例:

from collections.abc import Iterable, Iterator

def my_iterator(): 
    yield 1

i = my_iterator()
a = []

isinstance(i, Iterator) # True
isinstance(a, Iterator) # False

使my_iterator()成为Iterator的原因是同时具有__next____iter__魔法方法(顺便说一下,当您在collections.abc抽象基类上调用isinstance时,背后实际上发生了什么是测试是否存在某些魔法方法)。
请注意,迭代器也是Iterable,空列表也是(即两者都具有__iter__魔法方法):
isinstance(i, Iterable) # True
isinstance(a, Iterable) # True

此外需要注意,当你对两者都应用通用的iter()函数时,你会得到一个迭代器:正如Martijn Pieters的回答中所指出的
isinstance(iter(my_iterator()), Iterator) # True
isinstance(iter([])), Iterator) # True

这里[]my_iterator()的区别在于,iter(my_iterator())作为迭代器返回自身,而iter([])每次都会产生一个新的迭代器
正如MP在同样的答案中已经提到的那样,您上面的对象不是“迭代器容器”。它是一个可迭代对象,即“可迭代对象”。它是否“包含”某些内容并不相关;包含的概念由抽象基类Container表示。 Container可以是可迭代的,但不一定要是。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接