我没有完全掌握python迭代器, 我有一个带有子列表的对象,我想迭代这个结构。 我想获得与printall函数相同的行为,但使用迭代器。
class t:
def __init__(self, i):
self.l = []
self.a = 0
for ii in range(i):
self.a = ii
self.l.append(t(i-1))
def __iter__(self):
return self
def next(self):
for i in self.l:
yield i.__iter__()
yield self
def printall(self):
for i in self.l:
i.printall()
print self.a
希望有足够的信息,谢谢
编辑:
我只是希望能够遍历树的所有叶子并对对象做一些事情,即当我有一个实例时
bla = t(3)
我希望能够通过
遍历每个节点 for x in bla:
print x.a
例如,。我想能够与每个x, 我只需要访问每个孩子一次
答案 0 :(得分:18)
听起来你希望迭代器充当树遍历。研究itertools
模块,你真的可以去。
from itertools import chain, imap
class t:
def __init__(self, value):
self.value = value
self.children = []
def __iter__(self):
"implement the iterator protocol"
for v in chain(*imap(iter, self.children)):
yield v
yield self.value
root = t(0)
root.children.append(t(1))
root.children.append(t(2))
root.children[1].children.append(t(3))
print list(iter(root)) # -> [1, 3, 2, 0]
print list(iter(root.children[1])) # -> [3, 2]
编辑:以下是最初接受的实施方式。它有性能问题;我会删除它,但删除已接受答案的内容似乎是错误的。它将完全遍历整个结构,在生成任何值之前创建O(N*log[M](N))
生成器对象(对于具有包含M
总元素的分支因子N
的平衡树)。但它通过简单的表达确实产生了预期的结果。
(上述实现按需访问树的区域,并且一次只在内存中有O(M+log[M](N))
个生成器对象。在这两种实现中,只需要O(log[M](N))
级别的嵌套生成器。)
from itertools import chain
def isingle(item):
"iterator that yields only a single value then stops, for chaining"
yield item
class t:
# copy __init__ from above
def __iter__(self):
"implement the iterator protocol"
return chain(*(map(iter, self.children) + [isingle(self.value)]))
答案 1 :(得分:8)
根据您发布的代码,很明显您缺少的是生成器的功能,以及 __iter__
和next
的行为方式
所以让我们从迭代器协议开始。如果一个对象在调用其__iter__
方法时返回一个迭代器,则该对象是可迭代的,而迭代器是一个具有next
方法的对象,该方法可以被调用零次或多次,最终应该提升{{ 1}}。
某些类型的对象是他们自己的迭代器(StopIteration
返回__iter__
)并不罕见,但这通常仅限于以某种方式表示某事物内部位置的对象。例如,内置self
对象是它自己的迭代器,因为文件具有内在的搜索位置(您可以使用file
和file.seek()
进行操作)。其他对象代表集合的整体,如file.tell()
,返回的不是自己的东西。
所以,你的树听起来更像是后者而不是前者;它没有一个position属性来表示它所在的节点;它同时是所有节点,所以它可能不应该有list
方法; next()
需要返回其他内容。
这让我们成为发电机。当普通函数包含__iter__
语句时,它根本不是一个函数,它是一个生成器。不同的是,当你调用一个函数时,它的主体被执行(并且可能返回一个值)。当你调用一个生成器时,它会立即返回,而根本不执行主体;相反,你得到一个迭代器!当你迭代它时,函数体被调用;每次前进到下一个yield
,直到它最终返回。
所以,把它们放在一起,
yield
但是,因为我们正在做的是迭代一系列迭代器(还有一件事,自我),class t:
def __init__(self):
self.l = []
self.a = 0
def __iter__(self):
# first, yield everthing every one of the child nodes would yield.
for child in self.l:
for item in child:
# the two for loops is because there's multiple children, and we need to iterate
# over each one.
yield item
# finally, yield self
yield self
就像在接受的答案中一样,真的很有意义。
答案 2 :(得分:4)
我的第一个建议是在PEP-8之后更清楚地更改班级名称。管理诸如t
:
class Tree:
def __init__(self, i):
self.l = []
self.a = 0
for ii in range(i):
self.a = ii
self.l.append(Tree(i-1))
现在,您应该更改__iter__()
方法以返回self
中的下一个元素,而不是self
本身 - 没有任何双关语:) __iter__()
方法应返回原始对象的迭代器,而不是对象本身:
def __iter__(self):
return next(self)
现在出现了困难的部分:next()
方法。我总是觉得编写一个递归迭代器很难,但这并不是不可能的:对于每个子节点,迭代它并产生迭代值:
def next(self):
for i in self.l:
for ii in i:
yield ii
yield self
由于该方法是递归的,因此它负责产生所有后代。当在叶子节点(没有子节点的节点)上调用next()
方法时,它将只返回节点本身。 OTOH,当在具有子节点的节点上调用时,它将为每个子节点调用自己并产生返回的值。这意味着它将由子节点的子节点调用,直到叶节点为止。在被一个节点的所有后代调用之后 - 这意味着所有后代都被生成 - 它应该产生它自己的值,所以你必须自己产生原始节点。
现在你的printall()
功能应该完美无缺:
if __name__ == "__main__":
t = Tree(6)
t.printall()
最后一些考虑因素:
始终让您的课程扩展object
:
class Tree(object)::
我打赌你想写一个__init__()
方法,如下所示:
def __init__(self, i):
self.l = []
self.a = i
for ii in range(i):
self.l.append(Tree(i-1))
wberry解决方案更好,因为它更简洁,可能更有效。但是,我认为OP正在研究树木,递归等等。所以我认为一个更加硬编码的解决方案是有益的:)