详解Python3中的迭代器和生成器及其区别


Posted in Python onOctober 09, 2018

介绍

本篇将介绍Python3中的迭代器与生成器,描述可迭代与迭代器关系,并实现自定义类的迭代器模式。

迭代的概念

上一次输出的结果为下一次输入的初始值,重复的过程称为迭代,每次重复即一次迭代,并且每次迭代的结果是下一次迭代的初始值

注:循环不是迭代

while True: #只满足重复,因而不是迭代
print('====>')

 迭代器

1.为什么要有迭代器?

对于没有索引的数据类型,必须提供一种不依赖索引的迭代方式。

2.迭代器定义:

迭代器:可迭代对象执行__iter__方法,得到的结果就是迭代器,迭代器对象有__next__方法

它是一个带状态的对象,他能在你调用next()方法的时候返回容器中的下一个值,任何实现了__iter__和__next__()方法的对象都是迭代器,__iter__返回迭代器自身,__next__返回容器中的下一个值,如果容器中没有更多元素了,则抛出StopIteration异常

可迭代的(iterable)

Python标准库中存在着一些可迭代对象,例如:list, tuple, dict, set, str等。

可以对这些迭代对象,进行for-in等迭代操作,例如:

for s in "helloworld":
  print(s)

编译器若想迭代一个对象a,则会自动调用iter(a)获取该对象的迭代器(iterator),如果iter(a)抛出异常,则对象a不可迭代。

判断对象是否可迭代

原生函数iter(instance) 可以判断某个对象是否可迭代,它的工作流程大概分为以下3个步骤:

  • 检查对象instance是否实现了__iter__方法,并调用它获取返回的迭代器(iterator)。
  • 如果对象没有实现__iter__方法,但是实现了__getitem__方法,Python会生成一个迭代器。
  • 如果上述都失败,则编译器则抛出TypeError错误,‘xxx' Object is not iterable。

自定义类实现__iter__方法

根据第一条,我们自定义类Iter1实现__iter__方法使该类的对象可迭代。

class Iter1:
  def __init__(self, text):
    self.text = text

  def __iter__(self):
    return iter(self.text)

iter1 = Iter1("hello")
for s in iter1:
  print(s)

Iter1类实现了__iter__方法,通过iter()调用,得到可迭代对象text的迭代器并返回,实现了迭代器协议,因此可以通过for-in等方式对该对象进行迭代。

第二条通常都是针对Python中的序列(sequence)而定义,例如list,为了实现sequence协议,需要实现__getitem__方法。

class Iter2:
  def __init__(self, sequence):
    self.sequence = sequence

  def __getitem__(self, item):
    return self.sequence[item]


iter2 = Iter2([1, 2, 3, 4])
for s in iter2:
  print(s)

实际上,为了避免版本后序改动,Python标准库中的序列除了实现了__getitem__方法,也实现了__iter__方法,因此我们在定义序列时也应实现__iter__。

综上,如果显示判断某个对象是否可迭代,应该调用iter(instance)是否抛出异常,因为只实现了__getitem__的序列也是可迭代的(例子中Iter2的对象是可迭代的,但isinstance(iter2, abc.Iterator)返回结果是False)。同时,如果在调用iter后进行迭代操作不必显示判断,可以用try/except方式包装代码块。

iterable vs iterator(可迭代vs迭代器)

iterable定义

任何可以由原生函数iter获取到迭代器的对象
任何实现了__iter__方法并返回迭代器的对象
所有的序列(实现了__getitem__)

Python通过获取到可迭代对象的迭代器(iterator)实现迭代,例如for-in的实现其实是在内部获取到了迭代器进行操作。for-in机制可以理解为下述代码:

s = 'hello'
it = iter(s)
while (True):
  try:
    print(next(it))
  except StopIteration:
    del it
    break

StopIteration异常将在迭代器耗尽后被抛出,for-in、生成式(comprehension)、元组解压(tuple unpacking)等迭代操作都会处理并这个异常。

迭代器是个迭代值生产工厂,它保存迭代状态,并通过next()函数产生下一个迭代值。实现迭代器需要实现以下两个方法:

__iter__
返回self

__next__
返回下一个可用的元素,如果无可用元素则抛出StopIteration异常

迭代器实现__iter__,因此所有的迭代器都是可迭代的,下图展示了iterable和iterator的结构。

详解Python3中的迭代器和生成器及其区别

迭代器模式

实现一个自定义的迭代器模式需要两个类,分别为实现了__iter__方法的类和通过__iter__返回的迭代器实例类(实现了__iter__和__next__方法)。下面例子简单实现了上述功能。

class IterText:
  def __init__(self, text):
    self.text = text

  def __iter__(self):
    return IteratorText(self.text)


class IteratorText:
  def __init__(self, text):
    self.text = text
    self.index = 0

  def __iter__(self):
    return self

  def __next__(self):
    try:
      letter = self.text[self.index]
    except IndexError:
      raise StopIteration
    self.index += 1
    return letter

text = IterText("hey")
for l in text:
  print(l)

可迭代的IterText实现了__iter__方法,返回了迭代器IteratorText实例。IteratorText实现了__next__方法返回下一个迭代元素直到抛出异常,同时IteratorText实现了__iter__方法返回自身对象用于迭代。
这里的IterText和IteratorText很容易混淆,如果在IterText中实现了__next__方法并将__iter__中返回自身实例self也可以实现上述功能,但通常可迭代对象和迭代器应当分开,这样在可迭代对象中的__iter__中可以返回不同的迭代器对象,使功能独立。

生成器(generator)

通过上述文章说明,迭代器通过next()不断产出下一个元素直到迭代器耗尽,而Python中的生成器可以理解为一个更优雅的迭代器(不需要实现__iter__和__next__方法),实现了迭代器协议,它也可以通过next()产出元素。
Python中的生成器主要分为两种类型:

生成器函数(generator function)返回得到的生成器:

包含yield关键字的函数称为生成器函数

def gen_func():
  yield 1
  yield 2
  yield 3
g = gen_func()

生成器表达式(generator expression)返回得到的生成器

g = (i for i in (1, 2, 3))

我们可以利用生成器进行迭代操作:

for e in g:
  print(e)
  
## 生成器g已被耗尽,如果需要重新迭代需要重新获得新的生成器对象
g = gen_func()
for e in g:
  print(e)

利用生成器代替可迭代中的__iter__迭代器

在迭代器模式章节中,我们在可迭代IterText中的__iter__返回迭代器IteratorText实例,然而使用生成器的方式会使代码更加优雅。

class IterText:
  def __init__(self, text):
    self.text = text

  def __iter__(self):
    for letter in self.text:
      yield letter

因为yield存在于__iter__,因此__iter__变成了生成器函数,调用它测返回一个生成器,同时生成器又实现了迭代器协议,因此IterText满足了可迭代的需求。

总结

本篇介绍了Python中的可迭代(iterable)、迭代器(iterator)以及它们的关系,并讲述了迭代器模式的实现,同时通过Python中的生成器完善了迭代器模式。希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python with statement 进行文件操作指南
Aug 22 Python
python字典多键值及重复键值的使用方法(详解)
Oct 31 Python
用Python下载一个网页保存为本地的HTML文件实例
May 21 Python
python中的for循环
Sep 28 Python
django主动抛出403异常的方法详解
Jan 04 Python
python实现简单成绩录入系统
Sep 19 Python
python实现按关键字筛选日志文件
Dec 24 Python
TensorFlow tensor的拼接实例
Jan 19 Python
使用Python脚本从文件读取数据代码实例
Jan 19 Python
深入了解Python 方法之类方法 & 静态方法
Aug 17 Python
Python+OpenCV图像处理——打印图片属性、设置存储路径、调用摄像头
Oct 22 Python
Python之qq自动发消息的示例代码
Feb 18 Python
不知道这5种下划线的含义,你就不算真的会Python!
Oct 09 #Python
详解利用django中间件django.middleware.csrf.CsrfViewMiddleware防止csrf攻击
Oct 09 #Python
详解如何将python3.6软件的py文件打包成exe程序
Oct 09 #Python
让代码变得更易维护的7个Python库
Oct 09 #Python
windows下cx_Freeze生成Python可执行程序的详细步骤
Oct 09 #Python
Python打包方法Pyinstaller的使用
Oct 09 #Python
Python如何发布程序的详细教程
Oct 09 #Python
You might like
phpStudy访问速度慢和启动失败的解决办法
2015/11/19 PHP
JavaScript 学习笔记(七)字符串的连接
2009/12/31 Javascript
Whatever:hover 无需javascript让IE支持丰富伪类
2010/06/29 Javascript
jQuery中读取json文件示例代码
2013/05/10 Javascript
jquery实现简单易懂的图片展示小例子
2013/11/21 Javascript
jQuery消息提示框插件Tipso
2015/05/04 Javascript
AngularJS在IE下取数据总是缓存问题的解决方法
2016/08/05 Javascript
js 实现一些跨浏览器的事件方法详解及实例
2016/10/27 Javascript
jQuery 移动端拖拽(模块化开发,触摸事件,webpack)
2016/10/28 Javascript
jQuery编写设置和获取颜色的插件
2017/01/09 Javascript
js中的事件委托或是事件代理使用详解
2017/06/23 Javascript
vue通过指令(directives)实现点击空白处收起下拉框
2018/12/06 Javascript
JavaScript使用百度ECharts插件绘制饼图操作示例
2019/11/26 Javascript
ES5新增数组的实现方法
2020/05/12 Javascript
Python中的fileinput模块的简单实用示例
2015/07/09 Python
python实现各进制转换的总结大全
2017/06/18 Python
python探索之BaseHTTPServer-实现Web服务器介绍
2017/10/28 Python
给你选择Python语言实现机器学习算法的三大理由
2017/11/15 Python
Python生成8位随机字符串的方法分析
2017/12/05 Python
Ubuntu下使用Python实现游戏制作中的切分图片功能
2018/03/30 Python
Python统计python文件中代码,注释及空白对应的行数示例【测试可用】
2018/07/25 Python
利用Python将文本中的中英文分离方法
2018/10/31 Python
Python Selenium 之关闭窗口close与quit的方法
2019/02/13 Python
PyTorch 随机数生成占用 CPU 过高的解决方法
2020/01/13 Python
为什么说python适合写爬虫
2020/06/11 Python
Python爬虫入门教程01之爬取豆瓣Top电影
2021/01/24 Python
TripAdvisor越南:全球领先的旅游网站
2017/09/21 全球购物
MAC Cosmetics巴西官方网站:M·A·C彩妆
2019/04/18 全球购物
罗马尼亚在线杂货店:Pilulka.ro
2019/09/28 全球购物
出国英文推荐信
2014/05/10 职场文书
119消防日活动总结
2014/08/29 职场文书
房屋买卖委托书格式范本格式
2014/10/13 职场文书
焦裕禄纪念馆观后感
2015/06/09 职场文书
初中数学课堂教学反思
2016/02/17 职场文书
MySQL8.0.18配置多主一从
2021/06/21 MySQL
Python经常使用的一些内置函数
2022/04/11 Python