Python中yield的使用小述

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/teaspring/article/details/38733687

Python中yield恐怕是最迷人的特性之一了,不过要想理解到位也需要费点功夫。官方有一份比较详细的介绍,是基于v2.5.2的。虽然较长,不过建议耐心看完。本文只是想说多一些个人理解。

Python支持yield,主要是为了解决C++/Java中常见的回调函数(callback)问题,不得不说,Python的yield方式比callback要优雅得多。

函数体中如果用了yield, 那么它就变成一个函数生成器,而非直接返回值(或对象)的普通函数。确切的说,它返回的是一个迭代器。迭代器的典型用法比如:

for i in iter_01:
    print i
这里,如果有C++/Java基础的同学可能更习惯于以下写法:

for i in range(3):
    print i
注意:这里range(3)返回的是一个Python列表(list),但是这个列表并不是一般意义上C++/Java那样的数组,python里的列表实现了迭代器, 我们可以打开list的帮助文档:

>>> help(list)

...

 __iter__(...)

...

看见了吧,Python的list实际上实现了迭代器。所以请记住,Python里在in 出现的对象一定是被当作一个迭代器

这个有什么用?唔,它对yield的理解很重要。

我们看一个小例子。比如写一个递归的二叉树中序遍历函数,普通递归方法如下:

def inorder(root):
    if root:
        inorder(root.left)
        print root.val,
        inorder(root.right)
很简单对不对?它对于每次遍历到的节点直接做了标准输出。现在我们来做一些改变,如果我们希望返回一个列表,使得每个节点的使用留待外部决定,现在选择yield。

def inorder(root):
    if root:
        inorder(root.left)
        yield root.val
        inorder(root.right)

 很快改好了,简单。运行一下试试?你会发现它没有返回所有节点,仅仅返回了root的值。问题出在哪呢?是递归没有执行么?

如果我们认为每一条yield语句就返回迭代器的一个值,那么上面的代码就应该是对的。很遗憾,恰恰就错在这。yield真正的意义是使得包含yield的函数成为一个返回迭代器的生成器而每条yield语句其实是往这个迭代器里‘push’一个元素, 这个元素就是yield后面紧跟的表达式的值。所以,在函数体内,我们要对每一次该函数的递归调用,都使用for-in语句遍历一次迭代器。改正过的函数如下:

def inorder(root):
    if root:
        for x in inorder(root.left):
            yield x
        yield root.val
        for x in inorder(root.right):
            yield x

小结:

1. 含有yield语句的函数成为一个生成器,它返回一个迭代器。

2. 对于迭代器的遍历,安全方法是for-in语句,记住in后面紧跟的表达式都被视为迭代器。

2. 对于生成器函数的递归调用,每当递归调用发生时,必须进行一次for-in遍历

猜你喜欢

转载自blog.csdn.net/teaspring/article/details/38733687
今日推荐