python迭代器通过树与子列表

时间:2011-08-02 15:47:55

标签: python iterator

我没有完全掌握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, 我只需要访问每个孩子一次

3 个答案:

答案 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对象是它自己的迭代器,因为文件具有内在的搜索位置(您可以使用filefile.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正在研究树木,递归等等。所以我认为一个更加硬编码的解决方案是有益的:)