我没有完全掌握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都能做点什么, 我只需要接触每个孩子一次
听起来你希望迭代器充当树遍历。学习
itertools
模块,你就可以真正地去各个地方。编辑:下面是最初接受的实现。它有性能问题;我会删除它,但删除已被接受的答案的内容似乎是错误的。它将完全遍历整个结构,在生成任何值之前创建
O(N*log[M](N))
生成器对象(对于包含N
总元素的分支因子M
的平衡树)。但它确实通过一个简单的表达式产生了预期的结果。(上面的实现按需访问树的各个区域,并且一次只有
O(M+log[M](N))
个生成器对象在内存中。在这两种实现中,只需要O(log[M](N))
级别的嵌套生成器。)我的第一个建议是在PEP-8后面用一个更清晰的类来更改类的名称。管理类名(如
t
)有点困难:现在,您应该更改
__iter__()
方法以返回self
中的下一个元素,而不是self
本身-不需要双关:)方法__iter__()
应该返回一个迭代器到原始对象,而不是对象本身:接下来是困难的部分:方法
next()
。我总是发现很难编写递归迭代器,但这并非不可能:对于每个子级,对其进行迭代并生成迭代值:由于该方法是递归的,因此它负责生成所有子代。当在叶节点(没有子节点的节点)上调用
next()
方法时,它将只返回节点本身。OTOH,当在带有子节点的节点上调用时,它将为每个子节点调用自己并产生返回值。这意味着它将被子节点等的子节点调用,直到叶节点。在被一个节点的所有子节点调用后(这意味着所有子节点都已产生),它应该产生自己的值,因此必须产生原始节点本身。现在,您的
printall()
函数应该可以完美地工作了:一些最终考虑:
始终使类扩展
object
:类树(对象):
我打赌你想写一个
__init__()
方法,如下所示:wberry解决方案更好,因为它更简洁,而且可能更高效。但是,我认为OP正在研究树、递归等,所以我认为一个更硬编码的解决方案会有指导意义:)
从您发布的代码中可以清楚地看到,您缺少的是生成器的功能,以及如何
__iter__
和next
应该如何工作所以让我们从迭代器协议开始。如果一个对象在调用其
__iter__
方法时返回迭代器,并且迭代器是一个具有next
方法的对象,可以调用0次或多次,并且最终应该引发StopIteration
。某些类型的对象成为它们自己的迭代器(具有
__iter__
返回self
)并不罕见,但这通常仅限于对象本身以某种方式表示某个对象内部的位置。例如,内置的file
对象是它自己的迭代器,因为文件有一个内在的seek位置(可以使用file.seek()
和file.tell()
来操作)。其他表示集合整体性的对象,比如list
,返回的不是它们自己。所以,你的树听起来更像后者而不是前者;它没有表示它在哪个节点上的position属性;它同时是所有节点,所以它可能不应该有一个
next()
方法;__iter__
需要返回其他东西。把我们带到发电机那里。当一个普通函数包含一个
yield
语句时,它自动地不是一个函数,而是一个生成器。区别在于,当您调用一个函数时,它的主体被执行(并且可能返回一个值)。当您调用生成器时,它会立即返回,而根本不执行主体;相反,您会得到一个迭代器!当您遍历它时,函数体将被调用;每次都前进到下一个yield
,直到它最终返回。所以,把这些放在一起
但是,因为我们所做的是迭代一系列的迭代器(还有一件事,self),
itertools.chain
就像在接受的答案中一样,真的很有意义。相关问题 更多 >
编程相关推荐