Python学习笔记——生成器

作者: tcxurun 分类: Python 发布时间: 2014-11-28 00:03 ė 6 没有评论

迭代器相信大家都知道,迭代器就是重复地做一些事情,可以简单的理解为循环。在Python核心编程中说到:

根本上说,迭代器就是一个next()方法的对象,而不是通过索引来计数,当条目全部取出后,会引发一个StopIteration异常,这并不表示错误发生,只是告诉外部调用者,迭代完成。

迭代器有一些限制,不能向后移动,不能回到开始,很多时候使用迭代器完成的工作使用列表也可以完成,但是如果有很多值列表就会占用太多的内存。

那什么是Python式的生成器,在核心编程里说:

从语法上讲,生成器是一个带yield语句的函数。生成器是这样一个函数,它记住上一次返回时在函数体中的位置。对生成器函数的第二次(或第 n 次)调用跳转至该函数中间,而上次调用的所有局部变量都保持不变。

生成器就是一种迭代器,不过迭代器是一个对象,而生成器是一个函数。生成器拥有next方法并且行为与迭代器完全相同,这意味着生成器也可以用于Python的for循环中。但是你只可以读取它一次 ,因为它并不把所有的值放在内存中,它是实时地生成数据。

当建立了一个列表,可以逐项地读取这个列表,这叫做一个可迭代对象:

>>>mylist = [1, 2, 3]
>>>for i in mylist :
...     print(i)
1
2
3

mylist 是一个可迭代的对象。当使用一个列表生成式来建立一个列表的时候,就建立了一个可迭代的对象:

>>> mylist = [x*x for x in range(3)]
>>> for i in mylist :
...    print(i)
0
1
4

所有可以使用 for .. in .. 语法的叫做一个迭代器:链表,字符串,文件……我们经常使用它们是因为你可以如你所愿的读取其中的元素,但是这把所有的值都存储到了内存中,如果你有大量数据的话这个方式并不是你想要的。

mygenerator = (x*x for x in range(3))
for i in mygenerator :
    print(i)
0
1
4

第一次迭代中你的函数会执行,从开始到达 yield 关键字,然后返回 yield 后的值作为第一次迭代的返回值. 然后,每次执行这个函数都会继续执行你在函数内部定义的那个循环的下一次,再返回那个值,直到没有可以返回的。生成器函数在每次暂停执行时,函数体内的所有变量都将被封存(freeze)在生成器中,并将在恢复执行时还原,并且类似于闭包,即使是同一个生成器函数返回的生成器,封存的变量也是互相独立的。

举个例子说明:

>>> def fibonacci():
...     a = b = 1
...     yield a
...     yield b
...     while True:
...     a, b = b, a+b
...     yield b
...
>>> for num in fibonacci():
...     if num > 100: break
...     print num,
...
1 1 2 3 5 8 13 21 34 55 89

看到while True可别太吃惊,因为生成器可以挂起,所以是延迟计算的,无限循环并没有关系。这个例子中我们定义了一个生成器用于获取斐波那契数列。
生成器是函数,所以生成器也可以带参数,

>>> def counter(start_at=0):
...     count = start_at
...     while True:
...         val = (yield count)
...         if val is not None:
...             count = val
...         else:
...             count += 1

使用生成器最好的地方是正在迭代一个巨大的数据集合,如一个巨大的磁盘文件或者复杂的数据库查询。

def read_file(fpath): 
    BLOCK_SIZE = 1024
    with open(fpath, 'rb') as f: 
        while True: 
            block = f.read(BLOCK_SIZE) 
            if block: 
                yield block 
            else: 
                return

如果直接对文件对象调用 read() 方法,会导致不可预测的内存占用。好的方法是利用固定长度的缓冲区来不断读取文件内容。通过 yield,我们不再需要编写读文件的迭代类,就可以轻松实现文件读取。

本文出自天一直很蓝,转载时请注明出处及相应链接。

本文永久链接: http://www.tcxurun.cn/archives/321

发表评论

电子邮件地址不会被公开。 必填项已用*标注

Ɣ回顶部