刘毅同学

About Python, MySQL & Life

Python Yield小结

| Comments

yield 是用来简化以下场景:函数来生成序列,并且使用遍历的方式来访问序列中的元素。

yield的实现原理理解上来说在调用yield时Python会保留函数的现场,当再次遍历时函数的状态不丢失,可以继续生成。

经典的例子斐波那契数列

问题描述

返回斐波那契数列前n个元素

Python解法

第一个版本:朴素实现
1
2
3
4
5
6
7
8
9
def fab(n):
    fab_list = list()
    i = 0
    a, b = 0, 1
    while i < n:
        a, b = b, a+b
        fab_list.append(a)
        i = i + 1
    return fab_list

第一个版本是遍历并保存所有前n项斐波那契数列的元素。最大的问题是会占用非常多的内存,当调用fab(10000)时,在我的电脑中已经是无法完成的了。

第二个版本: 简单的迭代器实现

实现一个迭代器类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Fab(object):
    def __init__(self, n):
        self.n = n
        self.a, self.b = 0, 1
        self.i = 0

    def __iter__(self):
        return self

    def next(self):
        if self.i < self.n:
            r = self.b
            self.a, self.b = self.b, self.a + self.b
            self.i = self.i + 1
            return r
        raise StopIteration()

if __name__ == '__main__':
    for i in Fab(5):
        print i

第二个版本实现了迭代器,每次调用时再生成下一个元素,因此对内存的占用是恒定值。但缺点是代码很长,不够易读。

第三个版本:yield方法
1
2
3
4
5
6
7
8
9
10
def fab(n):
    i, a, b = 0, 0, 1
    while i < n:
        a, b = b, a+b
        yield a
        i = i + 1
if __name__ == '__main__':
    for n in fab(5):
        print n
    print fab(5)

第三种yield方法兼具了第一种的简洁,第二种的高效。

fab(5)返回的是一个generator对象——让一个函数像iterator那样工作,这样在遍历的场景下既可以保持代码简洁,又保持了内存使用的高效。

相关介绍链接

  1. Improve Your Python: ‘yield’ and Generators Explained
  2. Python yield 使用浅析

Comments