详解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调用机器喇叭发出蜂鸣声(Beep)的方法
Mar 23 Python
python获取当前计算机cpu数量的方法
Apr 18 Python
python常用知识梳理(必看篇)
Mar 23 Python
python实现超简单的视频对象提取功能
Jun 04 Python
详解python中*号的用法
Oct 21 Python
手把手教你Python yLab的绘制折线图的画法
Oct 23 Python
Python实现中值滤波去噪方式
Dec 18 Python
如何真正的了解python装饰器
Aug 14 Python
Python安装第三方库攻略(pip和Anaconda)
Oct 15 Python
python给list排序的简单方法
Dec 10 Python
使用Python制作一个数据预处理小工具(多种操作一键完成)
Feb 07 Python
用Python爬虫破解滑动验证码的案例解析
May 06 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
ajax php传递和接收变量实现思路及代码
2012/12/19 PHP
php实现HTML实体编号与非ASCII字符串相互转换类实例
2016/11/02 PHP
原来Jquery.load的方法可以一直load下去
2011/03/28 Javascript
jsp+javascript打造级连菜单的实例代码
2013/06/14 Javascript
JS 实现点击a标签的时候让其背景更换
2013/10/15 Javascript
扩展jQuery对象时如何扩展成员变量具体怎么实现
2014/04/25 Javascript
Nodejs中的this详解
2016/03/26 NodeJs
前端学习笔记style,currentStyle,getComputedStyle的用法与区别
2016/05/28 Javascript
第六篇Bootstrap表格样式介绍
2016/06/21 Javascript
js 创建对象 经典模式全面了解
2016/08/16 Javascript
AngularJs 利用百度地图API 定位当前位置 获取地址信息
2017/01/18 Javascript
从零学习node.js之利用express搭建简易论坛(七)
2017/02/25 Javascript
JS中Swiper的使用和轮播图效果
2017/08/11 Javascript
基于angular-utils-ui-breadcrumbs使用心得(分享)
2017/11/03 Javascript
vue-cli3.X快速创建项目的方法步骤
2019/11/14 Javascript
微信小程序实现发微博功能的示例代码
2020/06/24 Javascript
浅析JavaScript预编译和暗示全局变量
2020/09/03 Javascript
使用AutoJs实现微信抢红包的代码
2020/12/31 Javascript
[02:43]DOTA2英雄基础教程 德鲁伊
2014/01/13 DOTA
Python的装饰器用法学习笔记
2016/06/24 Python
使用sklearn进行对数据标准化、归一化以及将数据还原的方法
2018/07/11 Python
Python解析、提取url关键字的实例详解
2018/12/17 Python
Django url,从一个页面调到另个页面的方法
2019/08/21 Python
Python实现socket非阻塞通讯功能示例
2019/11/06 Python
Pytorch对Himmelblau函数的优化详解
2020/02/29 Python
django正续或者倒序查库实例
2020/05/19 Python
深入浅析Python代码规范性检测
2020/07/31 Python
Mavi牛仔裤美国官网:土耳其著名牛仔品牌
2016/09/24 全球购物
Lookfantastic意大利官网:英国知名美妆购物网站
2019/05/31 全球购物
经济学博士求职自荐信范文
2013/11/23 职场文书
实习生自荐信范文分享
2013/11/27 职场文书
商场中秋节广播稿
2014/01/17 职场文书
化学专业毕业生求职信
2014/07/28 职场文书
大学教师师德师风演讲稿
2014/08/22 职场文书
关于运动会的广播稿50字
2014/10/17 职场文书
一篇文章看懂MySQL主从复制与读写分离
2021/11/07 MySQL