论坛首页 编程语言技术论坛

从python的yield说起

浏览 11895 次
精华帖 (0) :: 良好帖 (2) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2010-02-03   最后修改:2010-02-03

前段时间在读trac 中wiki模块的源码的时候,发现了很多地方都使用了yiled这一关键词,

感觉是在需要返回某个值的地方通过yield来代替return,

不是很明白其用法,所以仔细研究下。

 

一个使用了yiled关键字的函数就不再是一个普通的函数了,而是一个生成器函数(generator function)

当函数被调用的时候将返回一个迭代器(iterator)。

 

所以下面将分别讲解迭代器和生成器这两个概念。

 

一. 迭代器(Iterator)

 

 写道
迭代器是一个对象,它实现了迭代器协议,
一般需要实现如下两个方法
1)next方法
返回容器的下一个元素
2)__iter__方法
返回迭代器自身
 

 

对于for语言大家可能都不陌生,我们很多时候需要对一些容器对象进行遍历就会使用到for循环:

 

l=[0,1,2,3,4,5,6]
for i in l:
    print i

 

l是一个type为list的对象,这段for-in代码在运行的时候其实是调用了l的__iter__()函数,返回了一个实现了__next__()或next()(各个版本的python可能不一样,我试验的时候所使用的版本为2.6.2)的迭代器对象,每循环一次就会通过next取下一个元素。

 

当然我们完全没有必要先把所有的元素都算出来放到一个list里或者其他容器类里进行循环,这样比较浪费空间。

我们可以直接创建自己的一个迭代器。

 

# -*- coding: utf-8 -*-

'''Fibonacci iterator'''

class Fib:
    '''一个可以生成Fibonacci 数列的迭代器'''

    def __init__(self, max):
        self.max = max

    def __iter__(self):
        self.a = 0
        self.b = 1
        return self

    def next(self):
        fib = self.a
        if fib > self.max:
            raise StopIteration
        self.a, self.b = self.b, self.a + self.b
        return fib
 

 

定义好了这个Fibonacci迭代器,我们就可以来使用它了。

 

 

from fibonacci2 import Fib
for n in Fib(1000):
    print n

 

当调用Fib(1000)的时候,将生成一个迭代器对象,每一次循环都将调用一次next取到下一个值。

所以我们可以看出迭代器有一个很核心的东西就是在循环中,迭代器可以记住之前的状态。

 

 

二.生成器

 

前面我们说了,任何使用了yield关键字的函数都不再是普通的函数了,我们还是来看实例吧,这样比较容易理解

 

 

def fib(max):
    a, b = 0, 1          
    while a < max:
        yield a          
        a, b = b, a + b  

   这里简单的几行代码就实现了上面的迭代器类那么一大堆代码所实现的功能

 

   使用的时候和上面很类似:

 

 

from fibonacci import fib
for n in fib(1000):
    print n

 

引文fib函数使用了yield所以它是一个生成器函数,当我们调用fib(1000)的时候它其实是返回了一个迭代器,且这个迭代器可以控制生成器函数的运行。

我们通过这个返回的迭代器的动作控制fib这个生成器函数的运行。

每当调用一次迭代器的next函数,生成器函数运行到yield之处,返回yield后面的值且在这个地方暂停,所有的状态都会被保持住,直到下次next函数被调用,或者碰到异常循环退出。

 

所以生成器的概念还是很简单的。

 

三.总结

 

1.for-in语句在底层都是对一个迭代器对象进行操作的

2.使用了yield关键字的函数就是一个生成器函数,被调用的时候生成一个可以控制自己运行的迭代器。

 

   发表时间:2010-02-03  
其实呢 yield可以从协程(Coroutines)这个角度来看待,生成器就好像一个生产者,而所关联的那个迭代器就是一个消费者。
0 请登录后投票
   发表时间:2010-02-11  
lukejin 写道
其实呢 yield可以从协程(Coroutines)这个角度来看待,生成器就好像一个生产者,而所关联的那个迭代器就是一个消费者。

而且还是双向的-w-

0 请登录后投票
   发表时间:2010-02-21  
yield是记住了多个断点的状态,那在性能上应该是比传统的迭代器速度慢
0 请登录后投票
   发表时间:2010-02-21  
calmness 写道
yield是记住了多个断点的状态,那在性能上应该是比传统的迭代器速度慢

但是比传统的方便很多的。
0 请登录后投票
   发表时间:2010-02-22   最后修改:2010-02-22
对PYTHON的理解还不是很深,感觉yield和RUBY的yield有点像。
敢问楼上各位我是否可以把这里的yield看做是一个template或者visitor里的一个留个未来实现的“抽象函数”?
0 请登录后投票
   发表时间:2010-02-26   最后修改:2010-02-26
使用yield未必会比自己写__init__,next速度慢,我也只是想当然,还没有实验过

python官方的tutorial也包含了yield的内容
9.10. Generators
0 请登录后投票
论坛首页 编程语言技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics