python使用yield来减少内存开销

本文参考自:http://www.ibm.com/developerworks/cn/opensource/os-cn-python-yield/

以斐波那契数列的实现来说明这个问题:

demo1:

def fab(max): 
    n, a, b = 0, 0, 1 
    L = [] 
    while n < max: 
        L.append(b) 
        a, b = b, a + b 
        n = n + 1 
    return L
for n in fab(5):
     print n
一般来讲,我们都会写成上边程序的样子,这样既实现了基本功能,又可以复用,基本符合要求了。

但是如果max是1000w或者更大呢,那么存储在list中将会占据很大的内存。常见的情况还包括一下消耗大量内存的做法:

for line in open("test.txt").readlines(): 
    print line
#这种做法是将文件的中的内容一次全部读取到内存中,每次从内存中取出一行然后输出,如果文件内容太大,将消耗大量的内存。
for line in open("test.txt"):
     print line
#这种做好就好多了,既简单又不节省内存,利用迭代器来每次读取一行数据。
for i in range(1000): pass
#会产生一个1000个元素的list
for i in xrange(1000): pass
#xrange()产生的是一个迭代对象,而不是一个1000个元素的list, 每次迭代返回下一个数值 
上面两个小对比,是我们平时写程序时不是很注意的地方,但是当你读到这之后,以后写程序不光要考虑怎么实现,而且要写出更优秀的代码来。

然后继续我们的斐波那契数列:

demo2:

class Fab(object): 

    def __init__(self, max): 
        self.max = max 
        self.n, self.a, self.b = 0, 0, 1 

    def __iter__(self): 
        return self 

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

for n in Fab(5): 
    print n 

 上面这个版本基本达到了我们的要求,实现基本功能+节省内存,利用函数next来实现每次输出下一个值, 但是这么小小的一个功能我们竟然写了这么多的代码,不够简洁!!! 
  

demo3:

def fab(max): 
    n, a, b = 0, 0, 1 
    while n < max: 
        yield b 
        a, b = b, a + b 
        n = n + 1 
for n in fab(5): 
    print n 
 
  
 简单来讲:yield 的作用就是把一个函数变成一个 generator,带有 yield 的函数不再是一个普通函数,Python 解释器会将其视为一个 generator,调用 fab(5) 不会执行 fab 函数,而是返回一个 iterable 对象!在 for 循环执行时,每次循环都会执行 fab 函数内部的代码,执行到 yield b 时,fab 函数就返回一个迭代值,下次迭代时,代码从 yield b 的下一条语句继续执行,而函数的本地变量看起来和上次中断执行前是完全一样的,于是函数继续执行,直到再次遇到 yield。(照抄的参考文章) 
  

个人理解: 是不是就是实时调用的意思,什么时候执行next什么时候执行一次fab函数,但是上一次的值都在内存中存储。而不是一次将所有的结果求出放在list中。

更多细节请参考本文一开头给出的文章链接, 下面我再补充一下关于迭代器和生成器的理解:

生成器:生成器(Generator)是创建迭代器的简单而强大的工具

迭代器:

    1、对于无法随机访问的数据结构(比如set)而言,迭代器是唯一的访问元素的方式

    2、它不要求你事先准备好整个迭代过程中所有的元素。迭代器仅仅在迭代至某个元素时才计算该元素,而在这之前或之后,元素可以不存在或者被销毁。这个特点使得它特别适合用于遍历一些巨大的或是无限的集合,比如几个G的文件,或是斐波那契数列等等。


具体的理解以及内部是如何工作的,大体可以参照:http://www.jb51.net/article/73939.htm

如果想更身体的了解迭代协议等内容,可以自行学习。










你可能感兴趣的:(每天一点python)