Python:用generator构造树和遍历(探究yield和yield from)

[0]:以前初步的了解了yield,今天在稍微深入的学习一下.

[1]首先含有yield关键字的函数就是协程,也是generator,注意,这个generator不再是函数的调用方式了,具体看一个例子

class Node(object):
    def __init__(self,value):
        self._left = []
        self._right = []
        self.value = value
    def iterate(self):
        for node in self._left:
            for value in node.iterate():yield value
        yield self.value
        for node in self._right:
            for value in node.iterate():yield value
    def dfs(self):##wrong!!!
        for node in self._left:node.iterate()
        yield self.value
        for node in self._right:node.iterate()

首先看dfs,我们定义了一个节点类,它有左右节点域,每个节点域都有很多节点.如果这个树很大,我们想在dfs函数之外利用它的中序遍历结果,可以用一个list不停添加访问的节点,但是内存开销很大,这个时候使用一个generator就很有用,它不是一下算出所有的节点。而是每次计算下一个节点.但是直接套用函数的方式来递归的访问是错误的.因为node.dfs()并不能递归的调用函数,而只能创建一个新的generator,你不去计算这个新的generator就不会继续调用.所以dfs这样是不能得到结果的.只能得到一个根节点.

正确的方式是如iterate()一样,强制访问新的iterate,保证计算出结果.注意由于不是函数递归调用,我们要加上两个yield(仔细思考为什么函数遍历只要一个print就好),如果不加,就像下面的代码,你会发现还是只能获得一个根节点,但是中序便利的顺序确被打印出来了.

def iterate(self):
        for node in self._left:
            for value in node.iterate():pass#yield value
        yield self.value
        print(self.value)
        for node in self._right:
            for value in node.iterate():pass#yield value

为什么?仔细想一想,对于node.iterate()这个generator的访问,我们确实获得了value,但是在node的父节点(self)产生的generator层面上,我们没有yield自节点的value,简单来说,我获得了,但是没有返回它!!!这里的区别很微妙.

======================================================================================

yield from的用法:

def iterate(self):
        for node in self._left:
            yield from node.iterate()
        yield self.value
        for node in self._right:
            yield from node.iterate()

它的好处之一就在于对from后面的对象调用iter(),然后不停的用next(),也就是说不需要写for循环来访问,而且会自动yield所有iter对象返回的东西.可以简化代码,同时也减少出错的概率.

yield from还有一个好处:首先看一下如果要用send传递参数的情况.这种情况下我们需要传递参数给generator,就要用send.

def iterate(self):
        for node in self._left:
            yield from node.iterate()
        input_value = yield self.value
        print(input_value)#也许需要inpute_value做点什么
        for node in self._right:
            yield from node.iterate()
total = l.send(None)
while True:
    try:
        ans = l.send(total)
        total += ans
    except StopIteration:
        break

但是如果我要重构代码,因为对左右子树的遍历的代码几乎是一样的,这时候yield from的优势体现出来了,代码非常简洁.另外,yield from 还可以直接传递参数,它的参数是从当前的generator获得的,这样就不必用send来控制了.

def children_iterate(self,nodes):
        for node in nodes:yield from node.iterate()
    def iterate(self):
        yield from self.children_iterate(self._left)
        input_value = yield self.value
        print(input_value)#也许需要inpute_value做点什么
        yield from self.children_iterate(self._right)

你可能感兴趣的:(Python:用generator构造树和遍历(探究yield和yield from))