如何在单个可迭代的python上同时具有多个迭代器?

强大

我想组合地比较可迭代对象中的所有元素。以下可重现的示例仅模仿纯列表的功能,但演示了我的问题。在此示例中,带有[“ A”,“ B”,“ C”,“ D”]的列表,我希望获得以下16行输出,每个项目彼此组合。100个项目的列表应生成100 * 100 = 10,000行。

A A True
A B False
A C False
... 10 more lines ...
D B False
D C False
D D True

以下代码似乎可以完成此工作。

class C():
    def __init__(self):
        self.stuff = ["A","B","C","D"]
    def __iter__(self):
        self.idx = 0
        return self
    def __next__(self):
        self.idx += 1
        if self.idx > len(self.stuff):
            raise StopIteration
        else:
            return self.stuff[self.idx - 1]

thing = C()
for x in thing:
    for y in thing:
        print(x, y, x==y)

但是,在完成y循环之后,即使只使用了iterable中的第一项,x循环也似乎完成了。

A A True
A B False
A C False
A D False

经过大量搜索之后,我最终尝试了以下代码,希望itertools.tee允许我对同一数据使用两个独立的迭代器:

import itertools
thing = C()
thing_one, thing_two = itertools.tee(thing)
for x in thing_one:
    for y in thing_two:
        print(x, y, x==y)

但是我得到了和以前一样的输出。

它代表的实际对象是目录和文件结构的模型,其中具有不同数量的文件和子目录,并且在树中的深度不同。就像这个示例一样,它嵌套了指向数千个成员的链接,并一次正确地对其进行了迭代。但是它也可以根据需要动态地在其许多内部对象中进行昂贵的处理,如果在迭代之前必须对其进行完整的复制,则最终会使工作量加倍。我真的很想使用多个迭代器,如果可能的话,指向所有数据的单个对象。


编辑答案:在所有答案中都指出,问题代码中的关键缺陷是单个内部self.idx变量无法独立处理多个调用者。可接受的答案是对我的真实班级最好的答案(在此可重现的示例中过分简化),另一个答案为更简单的数据结构(如此处显示的列表)提供了一种简单,优雅的解决方案。

塞弗特

实际上,不可能使容器类具有自己的迭代器。容器不应该知道迭代器的状态,并且迭代器不需要知道容器的内容,它只需要知道哪个对象是对应的容器以及“在哪里”即可。如果将迭代器和容器混合使用,则不同的迭代器将彼此共享状态(在您的情况下为self.idx),这不会给出正确的结果(它们读取并修改相同的变量)。

这就是为什么所有内置类型都具有单独的迭代器类(甚至有些具有反向迭代器类)的原因:

>>> l = [1, 2, 3]
>>> iter(l)
<list_iterator at 0x15e360c86d8>
>>> reversed(l)
<list_reverseiterator at 0x15e360a5940>

>>> t = (1, 2, 3)
>>> iter(t)
<tuple_iterator at 0x15e363fb320>

>>> s = '123'
>>> iter(s)
<str_iterator at 0x15e363fb438>

因此,基本上,您可以完全将其回去iter(self.stuff)因为知道如何遍历列表:__iter____next__list_iterator

class C:
    def __init__(self):
        self.stuff = ["A","B","C","D"]
    def __iter__(self):
        return iter(self.stuff)

thing = C()
for x in thing:
    for y in thing:
        print(x, y, x==y)

按预期打印16行。

如果您的目标是创建自己的迭代器类,则需要两个类(如果要自己实现反向迭代器,则需要3个类)。

class C:
    def __init__(self):
        self.stuff = ["A","B","C","D"]
    def __iter__(self):
        return C_iterator(self)
    def __reversed__(self):
        return C_reversed_iterator(self)

class C_iterator:
    def __init__(self, parent):
        self.idx = 0
        self.parent = parent
    def __iter__(self):
        return self
    def __next__(self):
        self.idx += 1
        if self.idx > len(self.parent.stuff):
            raise StopIteration
        else:
            return self.parent.stuff[self.idx - 1]

thing = C()
for x in thing:
    for y in thing:
        print(x, y, x==y)

也可以。

为了完整起见,这是反向迭代器的一种可能的实现:

class C_reversed_iterator:
    def __init__(self, parent):
        self.parent = parent
        self.idx = len(parent.stuff) + 1
    def __iter__(self):
        return self
    def __next__(self):
        self.idx -= 1
        if self.idx <= 0:
            raise StopIteration
        else:
            return self.parent.stuff[self.idx - 1]

thing = C()
for x in reversed(thing):
    for y in reversed(thing):
        print(x, y, x==y)

除了定义自己的迭代器外,还可以使用生成器。另一种答案已经显示了一种方法:

class C:
    def __init__(self):
        self.stuff = ["A","B","C","D"]
    def __iter__(self):
        yield from self.stuff
    def __reversed__(self):
        yield from self.stuff[::-1]

或显式委托给生成器函数(实际上与上面的函数等效,但可能更清楚地是它是生成的新对象):

def C_iterator(obj):
    for item in obj.stuff:
        yield item

def C_reverse_iterator(obj):
    for item in obj.stuff[::-1]:
        yield item

class C:
    def __init__(self):
        self.stuff = ["A","B","C","D"]
    def __iter__(self):
        return C_iterator(self)
    def __reversed__(self):
        return C_reverse_iterator(self)

注意:您不必实现__reversed__迭代器。那只是作为答案的附加“功能”。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

如何提示python3.6该参数必须同时具有大小和可迭代性?

具有可迭代和多个参数的Python多处理

如何在使用可初始化迭代器的同时从Tensorflow中的多个tfrecords中检索示例

Python,如何创建与随机可迭代对象具有相同类型的可迭代对象

如何在Rust中定义一个包含可迭代项的结构上的迭代器?

iText 7如何在Java中迭代时创建具有多个页面的单个pdf文件?

如何使用itertools对python中具有不同长度的可迭代对象进行迭代?

如何使一个对象同时具有Python2和Python3迭代器?

带有迭代器的Python zip函数与可迭代

如何迭代具有多个变量的命令?

如何在Python中的字母上创建循环迭代器?

如何在python中使数字可迭代?

在Python迭代器中具有hasNext?

如何在迭代器上循环?

如何在Python中迭代单个字符的可预测进度?

Python迭代器和可迭代列表

如何在Python中同时对多个不同的列表进行相同的迭代

如何在猫鼬的单个键中迭代多个值

如何在python中处理迭代器?

对于具有多个生存期参数的迭代器,impl迭代器失败

具有多个生存期的迭代器

如何返回具有多个生存期的impl迭代器?

如何在 Kotlin 中使用索引在可迭代对象上运行所有内容

如何在多个数组上创建可变循环迭代器

如何在 Python 中同时迭代和运行 AsyncGenerator

如何在Rust中返回盒装可克隆迭代器?

具有模板化类元素的向量上的迭代器

具有多个输入参数但只有一个可迭代的函数中的python多处理池

如何在for循环中的具有精确索引的元素上重复迭代?