详解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 返回汉字的汉语拼音
Feb 27 Python
在Python中使用pngquant压缩png图片的教程
Apr 09 Python
基于python时间处理方法(详解)
Aug 14 Python
浅谈Series和DataFrame中的sort_index方法
Jun 07 Python
python高效过滤出文件夹下指定文件名结尾的文件实例
Oct 21 Python
解决pycharm启动后总是不停的updating indices...indexing的问题
Nov 27 Python
python读取ini配置的类封装代码实例
Jan 08 Python
python中tkinter窗口位置\坐标\大小等实现示例
Jul 09 Python
Python使用requests模块爬取百度翻译
Aug 25 Python
python读取excel数据并且画图的实现示例
Feb 08 Python
Python 循环读取数据内存不足的解决方案
May 25 Python
Python爬虫实战之爬取京东商品数据并实实现数据可视化
Jun 07 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
PHP在Web开发领域的优势
2006/10/09 PHP
php中getservbyport与getservbyname函数用法实例
2014/11/18 PHP
php提示Warning:mysql_fetch_array() expects的解决方法
2014/12/16 PHP
php检索或者复制远程文件的方法
2015/03/13 PHP
php实现比较全的数据库操作类
2015/06/18 PHP
关于火狐(firefox)及ie下event获取的两种方法
2012/12/27 Javascript
js实现连续英文字符自动换行兼容ie6 ie7和firefox
2013/09/06 Javascript
javascript使用onclick事件改变选中行的颜色
2013/12/30 Javascript
滚动条响应鼠标滑轮事件实现上下滚动的js代码
2014/06/30 Javascript
简单实现jQuery弹幕效果
2017/05/06 jQuery
防止页面url缓存中ajax中post请求的处理方法
2017/10/10 Javascript
解决JQuery全选/反选第二次失效的问题
2017/10/11 jQuery
vue判断input输入内容全是空格的方法
2018/03/02 Javascript
javascript深拷贝、浅拷贝和循环引用深入理解
2018/05/27 Javascript
Vue-不允许嵌套式的渲染方法
2018/09/13 Javascript
vue实现点击追加选中样式效果
2019/11/01 Javascript
Vue实现跑马灯效果
2020/05/25 Javascript
js实现表格单列按字母排序
2020/08/12 Javascript
element-plus一个vue3.xUI框架(element-ui的3.x 版初体验)
2020/12/02 Vue.js
[40:29]2018DOTA2亚洲邀请赛 4.7总决赛 LGD vs Mineski 第一场
2018/04/10 DOTA
python创建文件备份的脚本
2018/09/11 Python
Python高级特性 切片 迭代解析
2019/08/23 Python
Django 博客实现简单的全文搜索的示例代码
2020/02/17 Python
PyCharm最新激活码PyCharm2020.2.3有效
2020/11/18 Python
全网最详细的PyCharm+Anaconda的安装过程图解
2021/01/25 Python
Canvas 文本填充线性渐变的使用详解
2020/06/22 HTML / CSS
设计师家具购买和委托在线市场:Viyet
2016/11/16 全球购物
英国足球店:UK Soccer Shop
2017/11/19 全球购物
触发器(trigger)的功能都有哪些?写出一个触发器的例子
2012/09/17 面试题
建筑装饰学院室内设计专业个人自我评价
2013/12/07 职场文书
小学运动会表扬稿
2014/01/19 职场文书
杨善洲电影观后感
2015/06/04 职场文书
演讲比赛主持词
2015/06/29 职场文书
选择比努力更重要?这是长期以来对“努力”的最大误解
2019/07/12 职场文书
python随机打印成绩排名表
2021/06/23 Python
JavaScript中document.activeELement焦点元素介绍
2021/11/27 Javascript