python的迭代器与生成器实例详解


Posted in Python onJuly 16, 2014

本文以实例详解了python的迭代器与生成器,具体如下所示:

1. 迭代器概述:
 
迭代器是访问集合元素的一种方式。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退,不过这也没什么,因为人们很少在迭代途中往后退。
 
1.1 使用迭代器的优点
 
对于原生支持随机访问的数据结构(如tuple、list),迭代器和经典for循环的索引访问相比并无优势,反而丢失了索引值(可以使用内建函数enumerate()找回这个索引值)。但对于无法随机访问的数据结构(比如set)而言,迭代器是唯一的访问元素的方式。

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

迭代器更大的功劳是提供了一个统一的访问集合的接口,只要定义了__iter__()方法对象,就可以使用迭代器访问。
 
迭代器有两个基本的方法
 
next方法:返回迭代器的下一个元素
__iter__方法:返回迭代器对象本身
下面用生成斐波那契数列为例子,说明为何用迭代器
 
示例代码1

def fab(max): 
  n, a, b = 0, 0, 1 
  while n < max: 
    print b 
    a, b = b, a + b 
    n = n + 1

直接在函数fab(max)中用print打印会导致函数的可复用性变差,因为fab返回None。其他函数无法获得fab函数返回的数列。
 
示例代码2

def fab(max): 
  L = []
  n, a, b = 0, 0, 1 
  while n < max: 
    L.append(b) 
    a, b = b, a + b 
    n = n + 1
  return L

代码2满足了可复用性的需求,但是占用了内存空间,最好不要。
 
示例代码3
 
对比:
 

for i in range(1000): pass
for i in xrange(1000): pass

前一个返回1000个元素的列表,而后一个在每次迭代中返回一个元素,因此可以使用迭代器来解决复用可占空间的问题
 

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 key in Fabs(5):
  print key

Fabs 类通过 next() 不断返回数列的下一个数,内存占用始终为常数

1.2 使用迭代器

使用内建的工厂函数iter(iterable)可以获取迭代器对象:

>>> lst = range(5)
>>> it = iter(lst)
>>> it
<listiterator object at 0x01A63110>

使用next()方法可以访问下一个元素:

>>> it.next()
 
>>> it.next()
 
>>> it.next()

python处理迭代器越界是抛出StopIteration异常

>>> it.next()
 
>>> it.next
<method-wrapper 'next' of listiterator object at 0x01A63110>
>>> it.next()
 
>>> it.next()
 
Traceback (most recent call last):
 File "<pyshell#27>", line 1, in <module>
  it.next()
StopIteration

了解了StopIteration,可以使用迭代器进行遍历了

lst = range(5)
it = iter(lst)
try:
  while True:
    val = it.next()
    print val
except StopIteration:
  pass

事实上,因为迭代器如此普遍,python专门为for关键字做了迭代器的语法糖。在for循环中,Python将自动调用工厂函数iter()获得迭代器,自动调用next()获取元素,还完成了检查StopIteration异常的工作。如下

>>> a = (1, 2, 3, 4)
>>> for key in a:
  print key

首先python对关键字in后的对象调用iter函数迭代器,然后调用迭代器的next方法获得元素,直到抛出StopIteration异常。

1.3 定义迭代器
 
下面一个例子——斐波那契数列
 

# -*- coding: cp936 -*-
class Fabs(object):
  def __init__(self,max):
    self.max = max
    self.n, self.a, self.b = 0, 0, 1 #特别指出:第0项是0,第1项是第一个1.整个数列从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()
 
print Fabs(5)
for key in Fabs(5):
  print key

结果

<__main__.Fabs object at 0x01A63090>

2. 迭代器

带有 yield 的函数在 Python 中被称之为 generator(生成器),几个例子说明下(还是用生成斐波那契数列说明)
 
可以看出代码3远没有代码1简洁,生成器(yield)既可以保持代码1的简洁性,又可以保持代码3的效果
 
示例代码4 
 

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。看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当前的迭代值。
 
也可以手动调用 fab(5) 的 next() 方法(因为 fab(5) 是一个 generator 对象,该对象具有 next() 方法),这样我们就可以更清楚地看到 fab 的执行流程:

>>> f = fab(3)
>>> f.next()
1
>>> f.next()
1
>>> f.next()
2
>>> f.next()
 
Traceback (most recent call last):
 File "<pyshell#62>", line 1, in <module>
  f.next()
StopIteration

return作用

在一个生成器中,如果没有return,则默认执行到函数完毕;如果遇到return,如果在执行过程中 return,则直接抛出 StopIteration 终止迭代。例如
 

>>> s = fab(5)
>>> s.next()
1
>>> s.next()
 
Traceback (most recent call last):
 File "<pyshell#66>", line 1, in <module>
  s.next()
StopIteration

示例代码5  文件读取

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,我们不再需要编写读文件的迭代类,就可以轻松实现文件读取。

Python 相关文章推荐
Python入门篇之面向对象
Oct 20 Python
Python2.x版本中cmp()方法的使用教程
May 14 Python
利用Python读取txt文档的方法讲解
Jun 23 Python
Pandas库之DataFrame使用的学习笔记
Jun 21 Python
基于python二叉树的构造和打印例子
Aug 09 Python
Django CBV与FBV原理及实例详解
Aug 12 Python
python文件和文件夹复制函数
Feb 07 Python
python右对齐的实例方法
Jul 05 Python
Matplotlib中%matplotlib inline如何使用
Jul 28 Python
Python通过format函数格式化显示值
Oct 17 Python
Python爬虫入门教程01之爬取豆瓣Top电影
Jan 24 Python
Python os和os.path模块详情
Apr 02 Python
Python的内存泄漏及gc模块的使用分析
Jul 16 #Python
Python的垃圾回收机制深入分析
Jul 16 #Python
python中将字典转换成其json字符串
Jul 16 #Python
记录Django开发心得
Jul 16 #Python
Python实现动态添加类的属性或成员函数的解决方法
Jul 16 #Python
Python重新引入被覆盖的自带function
Jul 16 #Python
Python实现扫描指定目录下的子目录及文件的方法
Jul 16 #Python
You might like
php中的MVC模式运用技巧
2007/05/03 PHP
ThinkPHP3.2.3框架Memcache缓存使用方法实例总结
2019/04/15 PHP
一个刚完成的layout(拖动流畅,不受iframe影响)
2007/08/17 Javascript
javascript 出生日期和身份证判断大全
2008/11/13 Javascript
jquery获得页面元素的坐标值实现思路及代码
2013/04/15 Javascript
js猜数字小游戏的简单实现代码
2013/07/02 Javascript
JS 实现Table相同行的单元格自动合并示例代码
2013/08/27 Javascript
不到30行JS代码实现Excel表格的方法
2014/11/15 Javascript
利用AJAX实现WordPress中的文章列表及评论的分页功能
2016/05/17 Javascript
javascript数组常用方法汇总
2016/09/10 Javascript
jQuery+CSS3实现点赞功能
2017/03/13 Javascript
js实现可以点击收缩或张开的悬浮窗
2017/09/18 Javascript
详细分析JS函数去抖和节流
2017/12/05 Javascript
React Native 真机断点调试+跨域资源加载出错问题的解决方法
2018/01/18 Javascript
微信小程序 腾讯地图显示偏差问题解决
2019/07/27 Javascript
layui的layedit富文本赋值方法
2019/09/18 Javascript
详解vue 自定义组件使用v-model 及探究其中原理
2019/10/11 Javascript
javascript二维数组和对象的深拷贝与浅拷贝实例分析
2019/10/26 Javascript
JavaScript ECMA-262-3 深入解析(一):执行上下文实例分析
2020/04/25 Javascript
Vue记住滚动条和实现下拉加载的完美方法
2020/07/31 Javascript
[08:42]DOTA2每周TOP10 精彩击杀集锦vol.2
2014/06/25 DOTA
[57:36]DOTA2-DPC中国联赛 正赛 SAG vs CDEC BO3 第三场 2月1日
2021/03/11 DOTA
Python中的元类编程入门指引
2015/04/15 Python
Python编程实现从字典中提取子集的方法分析
2018/02/09 Python
解读python如何实现决策树算法
2018/10/11 Python
Django安装配置mysql的方法步骤
2018/10/15 Python
在Python中使用defaultdict初始化字典以及应用方法
2018/10/31 Python
对python numpy.array插入一行或一列的方法详解
2019/01/29 Python
对Django的restful用法详解(自带的增删改查)
2019/08/28 Python
Python大数据之网络爬虫的post请求、get请求区别实例分析
2019/11/16 Python
Python协程 yield与协程greenlet简单用法示例
2019/11/22 Python
对Pytorch中Tensor的各种池化操作解析
2020/01/03 Python
keras实现theano和tensorflow训练的模型相互转换
2020/06/19 Python
HTML5组件Canvas实现图像灰度化(步骤+实例效果)
2013/04/22 HTML / CSS
美国在线购买空气净化器、除湿器、加湿器网站:AllergyBuyersClub
2021/03/16 全球购物
python爬取某网站原图作为壁纸
2021/06/02 Python