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库urllib与urllib2主要区别分析
Jul 13 Python
Django1.7+python 2.78+pycharm配置mysql数据库教程
Nov 18 Python
分享一个常用的Python模拟登陆类
Mar 29 Python
Python中的getopt函数使用详解
Jul 28 Python
Python操作SQLite数据库的方法详解【导入,创建,游标,增删改查等】
Jul 11 Python
python入门教程 python入门神图一张
Mar 05 Python
使用Flask集成bootstrap的方法
Jul 24 Python
pycharm运行和调试不显示结果的解决方法
Nov 30 Python
Python 格式化输出_String Formatting_控制小数点位数的实例详解
Feb 04 Python
Python Numpy中数据的常用保存与读取方法
Apr 01 Python
python 如何将两个实数矩阵合并为一个复数矩阵
May 19 Python
Python中使用tkFileDialog实现文件选择、保存和路径选择
May 20 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中通过虚代理实现延迟加载的实现代码
2011/06/10 PHP
基于php验证码函数的使用示例
2013/05/03 PHP
PHP对象、模式与实践之高级特性分析
2016/12/08 PHP
phpQuery采集网页实现代码实例
2020/04/02 PHP
JavaScript QueryString解析类代码
2010/01/17 Javascript
JavaScript加强之自定义event事件
2013/09/21 Javascript
js实现瀑布流的一种简单方法实例分享
2013/11/04 Javascript
使用js显示当前时间示例
2014/03/02 Javascript
JavaScript数字和字符串转换示例
2014/03/26 Javascript
js获取鼠标点击的位置实现思路及代码
2014/05/09 Javascript
IE下通过a实现location.href 获取referer的值
2014/09/04 Javascript
完美兼容IE,chrome,ff的设为首页、加入收藏及保存到桌面js代码
2014/12/17 Javascript
jQuery实现气球弹出框式的侧边导航菜单效果
2015/09/22 Javascript
JavaScript的Backbone.js框架的一些使用建议整理
2016/02/14 Javascript
深入理解Angularjs向指令传递数据双向绑定机制
2016/12/31 Javascript
bootstrap 下拉多选框进行多选传值问题代码分析
2017/02/14 Javascript
JS失效 提示HTML1114: (UNICODE 字节顺序标记)的代码页 utf-8 覆盖(META 标记)的冲突的代码页 utf-8
2017/06/23 Javascript
vue.js 实现点击展开收起动画效果
2018/07/07 Javascript
使用js实现将后台传入的json数据放在前台显示
2018/08/06 Javascript
vue打包相关细节整理(小结)
2018/09/28 Javascript
vue组件之间通信方式实例总结【8种方式】
2019/02/22 Javascript
浅探express路由和中间件的实现
2019/09/30 Javascript
详解javascript中var与ES6规范中let、const区别与用法
2020/01/11 Javascript
Vue实现浏览器打印功能的代码
2020/04/17 Javascript
Python中使用md5sum检查目录中相同文件代码分享
2015/02/02 Python
遍历python字典几种方法总结(推荐)
2016/09/11 Python
使用Python爬了4400条淘宝商品数据,竟发现了这些“潜规则”
2018/03/23 Python
python爬虫 批量下载zabbix文档代码实例
2019/08/21 Python
python中dict()的高级用法实现
2019/11/13 Python
Tensorflow限制CPU个数实例
2020/02/06 Python
Python利用PyPDF2库获取PDF文件总页码实例
2020/04/03 Python
浅谈优化Django ORM中的性能问题
2020/07/09 Python
详解px单位html5响应式方案
2018/03/08 HTML / CSS
Nicole Miller官方网站:纽约女装品牌
2019/09/14 全球购物
2015年实习单位评语
2015/03/25 职场文书
干部培训工作总结2015
2015/05/25 职场文书