如何在一个python iterable上同时拥有多个迭代器?
我想以组合方式比较我的iterable对象中的所有元素。下面的可复制示例只是模拟了普通列表的功能,但演示了我的问题。在这个例子中,有一个[“a”、“B”、“C”、“D”]的列表,我希望得到以下16行输出,每个项目的每个组合都是相互关联的。100个项目的列表应生成100*100=10000行如何在一个python iterable上同时拥有多个迭代器?,python,python-3.x,for-loop,combinatorics,itertools,Python,Python 3.x,For Loop,Combinatorics,Itertools,我想以组合方式比较我的iterable对象中的所有元素。下面的可复制示例只是模拟了普通列表的功能,但演示了我的问题。在这个例子中,有一个[“a”、“B”、“C”、“D”]的列表,我希望得到以下16行输出,每个项目的每个组合都是相互关联的。100个项目的列表应生成100*100=10000行 A A True A B False A C False ... 10 more lines ... D B False D C False D D True 下面的代码似乎应该完成这项工作 class C
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形环之后,x形环似乎也完成了,尽管它只用于iterable中的第一项
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变量无法独立处理多个呼叫者。被接受的答案对我的真实类来说是最好的(在这个可复制的例子中过于简单),另一个答案为更简单的数据结构提供了一个简单、优雅的解决方案,如这里列出的列表。您的
\uuuuuuuu iter\uuuuu
完全崩溃了。它不是在每次调用时实际生成一个新的迭代器,而是在self
上重置一些状态并返回self
。这意味着在对象上一次不能有多个迭代器,当对象上的另一个循环处于活动状态时,对\uu iter\uu
的任何调用都会干扰现有的循环
您需要实际创建一个新对象。最简单的方法是使用yield
语法编写生成器函数。生成器函数将在每次执行以下操作时自动返回一个新的迭代器对象:
class C(object):
def __init__(self):
self.stuff = ['A', 'B', 'C', 'D']
def __iter__(self):
for thing in self.stuff:
yield thing
实际上,不可能创建一个容器类作为自己的迭代器。容器不应该知道迭代器的状态,迭代器也不需要知道容器的内容,它只需要知道哪个对象是对应的容器以及它在哪里。如果混合使用迭代器和容器,不同的迭代器将彼此共享状态(在您的情况下是
self.idx
),这将不会给出正确的结果(它们读取和修改相同的变量)
这就是为什么所有内置类型都有一个单独的迭代器类(甚至有些有一个反向迭代器类):
按预期打印16行
如果您的目标是创建自己的迭代器类,则需要两个类(如果您想自己实现反向迭代器,则需要3个类)
同样有效
为了完整起见,这里有一个可能的反向迭代器实现:
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)
或者显式地委托给一个生成器函数(这实际上等同于上述内容,但可能更清楚地表明它是一个生成的新对象):
注意:您不必实现
迭代器。这只是答案的附加“功能”。无论是什么来源教你写这样的iterables,停止使用它。你的对象是可索引的吗?它有一个\uuu len\uuuu
方法吗?它本质上是一个嵌套目录和文件结构的表示,所以对于多个级别,我没有一个单独的索引。不过,我确实有一个节点总数,所以我可以轻松地编写一个len。它嵌套的深度有多深?是否总是只有两个级别?每个家长的子女数量总是一致的还是不同的?或者可以动态地找到该数字?一般规则:如果定义了\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
,则\uuuuuuuuuuu。如果不是,则代码是错误的。通常情况下,您并不想手动实现迭代器类,因此您只需将\uuuuuuiter\uuuuu
作为一个生成器函数,如本文所示,并避免完全实现\uuuuuuuu next\uuuu
。生成器函数方法比为您的类型定义一个单独的迭代器类更快(让Python管理生成器状态可以让它更高效地完成),也更简单。我需要弄清楚它们是否适用于我在对象中迭代多层次结构的用例。谢谢!我几乎可以肯定,我需要实现自己的迭代器,因为我的实际类需要一个接一个地迭代多个列表,对调用方来说是不透明的。我还在学习,但如果像收益率这样的发电机能够处理我的情况,我会感到惊讶。这个答案涵盖了让我到达我需要的地方的很多方面。干杯@mightypile:你说的一个接一个地迭代多个列表?
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)
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)