Python的迭代器和生成器


Posted in Python onJuly 29, 2015

先说迭代器,对于string、list、dict、tuple等这类容器对象,使用for循环遍历是很方便的。在后台for语句对容器对象调用iter()函数,iter()是python的内置函数。iter()会返回一个定义了next()方法的迭代器对象,它在容器中逐个访问容器内元素,next()也是python的内置函数。在没有后续元素时,next()会抛出一个StopIteration异常,通知for语句循环结束。比如:

>>> s = 'abc'
>>> it = iter(s)
>>> it
<str_iterator object at 0x7f71fefe9d68>
>>> next(it)
'a'
>>> next(it)
'b'
>>> next(it)
'c'
>>> next(it)
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
StopIteration

上面说的都是python自带的容器对象,它们都实现了相应的迭代器方法,那如果是自定义类需要遍历怎么办?方法很简单,对这个类AClass,实现一个__iter__(self)方法,使其返回一个带有__next__(self)方法的对象就可以了。如果你在AClass刚好也定义了__next__(self)方法(一般使用迭代器都会定义),那在__iter__里只要返回self就可以。废话少说,先上代码:

class Fib(object):
  def __init__(self, max):
    super(Fib, self).__init__()
    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

def main():
  fib = Fib(100)
  for i in fib:
    print(i)

if __name__ == '__main__':
  main()

简单讲下代码会干什么,定义了一个Fib类,用于生成fibonacci序列。用for遍历时会逐个打印生成的fibonacci数,max是生成的fibonacci序列中数字大小的上限。

在类的实现中,定义了一个__iter__(self)方法,这个方法是在遍历时被iter()调用,返回一个迭代器。因为在遍历的时候,是直接调用的python内置函数iter(),由iter()通过调用__iter__(self)获得对象的迭代器。有了迭代器,就可以逐个遍历元素了。而逐个遍历的时候,也是使用内置的next()函数通过调用对象的__next__(self)方法对迭代器对象进行遍历。所以要实现__iter__(self)和__next__(self)。而且因为实现了__next__(self),所以在实现__iter__(self)的时候,直接返回self就可以。

为了更好理解,我再简单重复下上面说的那一段:在循环遍历自定义容器对象时,会使用python内置函数iter()调用遍历对象的__iter__(self)获得一个迭代器,之后再循环对这个迭代器使用next()调用迭代器对象的__next__(self)。__iter__只会被调用一次,而__next__会被调用 n 次。

下面说生成器。

生成器(Generator)是创建迭代器的简单而强大的工具。它们写起来就像是正规的函数,只是在需要返回数据的时候使用yield语句。每次next()被调用时,生成器会返回它脱离的位置(它记忆语句最后一次执行的位置和所有的数据值)。以下示例演示了生成器可以很简单的创建出来:

>>> def reverse(data):
...   for index in range(len(data)-1, -1, -1):
...     yield data[index]
... 
>>> for char in reverse('hello'):
...   print(char)
... 
o
l
l
e
h

关于迭代器和生成器的区别,生成器能做到迭代器能做的所有事,而且因为自动创建了__iter__()和 next()方法,生成器显得特别简洁,而且生成器也是高效的。除了创建和保存程序状态的自动方法,当发生器终结时,还会自动抛出StopIteration异常。一个带有yield的函数就是一个 生成器,它和普通函数不同,生成一个 generator 看起来像函数调用,但不会执行任何函数代码,直到对其调用next()(在 for 循环中会自动调用next())才开始执行。虽然执行流程仍按函数的流程执行,但每执行到一个yield语句就会中断,并返回一个迭代值,下次执行时从yield的下一个语句继续执行。看起来就好像一个函数在正常执行的过程中被yield中断了数次,每次中断都会通过yield返回当前的迭代值(yield暂停一个函数,next()从其暂停处恢复其运行)。

另外对于生成器,python还提供了一个生成器表达式:类似与一个yield值的匿名函数。表达式本身看起来像列表推到, 但不是用方括号而是用圆括号包围起来:

>>> unique_characters = {'E', 'D', 'M', 'O', 'N', 'S', 'R', 'Y'}
>>> gen = (ord(c) for c in unique_characters)
>>> gen
<generator object <genexpr> at 0x7f2be4668678>
>>> for i in gen:
...   print(i)
... 
69
79
83
77
82
78
89
68
>>>

如果需要,可以将生成器表达式传给tuple、list或是set来迭代所有的值并且返回元组、列表或是集合。在这种情况下,不需要一对额外的括号 ———— 直接将生成器表达式 ord(c) for c in unique_characters传给tuple()等函数就可以了, Python 会推断出它是一个生成器表达式。

最后,为什么要使用生成器?因为效率。使用生成器表达式取代列表解析可以同时节省 cpu 和 内存(ram)。如果你构造一个列表的目的仅仅是传递给别的函数,(比如 传递给tuple()或者set()), 那就用生成器表达式替代吧!

以上所述就是本文的全部内容了,希望大家能够喜欢。

Python 相关文章推荐
Python struct模块解析
Jun 12 Python
Python中replace方法实例分析
Aug 20 Python
Python中的choice()方法使用详解
May 15 Python
Python中shutil模块的常用文件操作函数用法示例
Jul 05 Python
利用numpy实现一、二维数组的拼接简单代码示例
Dec 15 Python
基于Python log 的正确打开方式
Apr 28 Python
Opencv-Python图像透视变换cv2.warpPerspective的示例
Apr 11 Python
Python之数据序列化(json、pickle、shelve)详解
Aug 30 Python
解析python实现Lasso回归
Sep 11 Python
手把手教你安装Windows版本的Tensorflow
Mar 26 Python
Python select及selectors模块概念用法详解
Jun 22 Python
Python操作Word批量生成合同的实现示例
Aug 28 Python
在Python程序中操作MySQL的基本方法
Jul 29 #Python
Python操作Word批量生成文章的方法
Jul 28 #Python
Python实现批量转换文件编码的方法
Jul 28 #Python
Python中subprocess的简单使用示例
Jul 28 #Python
Python中文竖排显示的方法
Jul 28 #Python
Python中的getopt函数使用详解
Jul 28 #Python
Python3访问并下载网页内容的方法
Jul 28 #Python
You might like
PHP提取数据库内容中的图片地址并循环输出
2010/03/21 PHP
php循环检测目录是否存在并创建(循环创建目录)
2011/01/06 PHP
php.ini 配置文件的深入解析
2013/06/17 PHP
用HTML/JS/PHP方式实现页面延时跳转的简单实例
2016/07/18 PHP
PHP的CURL方法curl_setopt()函数案例介绍(抓取网页,POST数据)
2016/12/14 PHP
Yii1.1中通过Sql查询进行的分页操作方法
2017/03/16 PHP
关于 Laravel Redis 多个进程同时取队列问题详解
2017/12/25 PHP
PHP堆栈调试操作简单示例
2018/06/15 PHP
javascript 网页跳转的方法
2008/12/24 Javascript
Jquery 数据选择插件Pickerbox使用介绍
2012/08/24 Javascript
JavaScript判断DOM何时加载完毕的技巧
2012/11/11 Javascript
document.forms用法示例介绍
2014/06/26 Javascript
JavaScript中的return语句简单介绍
2015/12/07 Javascript
javascript实现tab响应式切换特效
2016/01/29 Javascript
jQuery ajax调用后台aspx后台文件的两种常见方法(不是ashx)
2016/06/28 Javascript
webpack入门必知必会
2017/01/16 Javascript
原生js实现验证码功能
2017/03/16 Javascript
jQuery进阶实践之利用最优雅的方式如何写ajax请求
2017/12/20 jQuery
解决vue 打包发布去#和页面空白的问题
2018/09/04 Javascript
vue中v-for循环给标签属性赋值的方法
2018/10/18 Javascript
微信小程序前端自定义分享的实现方法
2019/06/13 Javascript
jQuery操作元素追加内容示例
2020/01/10 jQuery
JS如何操作DOM基于表格动态展示数据
2020/10/15 Javascript
[01:58]2018DOTA2亚洲邀请赛趣味视频——交流
2018/04/03 DOTA
Python获取电脑硬件信息及状态的实现方法
2014/08/29 Python
Windows下搭建python开发环境详细步骤
2020/07/20 Python
python语音识别实践之百度语音API
2018/08/30 Python
python程序 线程队列queue使用方法解析
2019/09/23 Python
python3多线程知识点总结
2019/09/26 Python
HTML5实现应用程序缓存(Application Cache)
2020/06/16 HTML / CSS
N:Philanthropy官网:美国洛杉矶基础款服装
2020/06/09 全球购物
妇女干部培训方案
2014/05/12 职场文书
励志演讲稿大全
2014/08/21 职场文书
个人整改措施落实情况汇报
2014/10/29 职场文书
小学班主任教育随笔
2015/08/15 职场文书
Django路由层如何获取正确的url
2021/07/15 Python