Python迭代器

2023-10-27

  迭代器是在Python2.2中被加入的,它为类序列对象提供了一个类序列的接口,有了迭代器可以迭代一个不是序列的对象,因为它表现出了序列的行为。关于Python中的迭代器,有几个比较容易混淆的概念:可迭代对象(iterable)、迭代器(iterator)、生成器(generator),本文将对此做一个详细的讲解。


1、简介

  首先,我们用一张图来清晰地展示出几个概念之间的关系
在这里插入图片描述
通过上面这张图,可以得到:

  1. 列表/集合/字典推导式可以得到相应的容器对象
  2. 通常而言,容器对象都是可迭代对象
  3. 给内建函数iter()传入一个可迭代对象,可以得到一个迭代器;迭代器属于可迭代对象
  4. 给内建函数next()传入一个迭代器,可以实现惰性求值
  5. 生成器包括两种形式:生成器表达式和生成器函数;生成器属于迭代器

接下来,我们一一进行解释。

2、可迭代对象和迭代器

  首先,我们来了解一下迭代器协议:

  1. 迭代器协议是指:对象必须提供一个next方法,执行方法要么返回迭代器中的下一项,要么就引起一个StopIteration异常,以终止迭代(只能往后走,不能往前退)
  2. 可迭代对象:实现了迭代器协议的对象(实现方式:对象内部定义了一个__iter__方法)
  3. 协议是一种约定,可迭代对象实现了迭代器协议,Python的内部工具(如for循环,sum,min,max函数等)使用迭代器协议访问对象

  简单来说,如果一个对象实现了__iter__方法,其是可迭代对象,它的__iter__方法返回的是当前对象的迭代器类的实例;如果一个对象实现了__iter__next方法,其是迭代器,它的__iter__方法返回自身,表示自身即是自己的迭代器。我们来看一段代码:

# 定义可迭代对象
class MyIterable(object):
	def __init__(self, n):
		self.n = n
	def __iter__(self):
		return MyIterator(self.n)

# 定义迭代器:Fibonacci数列
class MyIterator(object):
	def __init__(self, n):
		self.n = n
		self.num = 0
		self.pre, self.post = 0, 1
	def __iter__(self):
		return self
	def next(self):
		if self.num < self.n:
			self.pre, self.post = self.post, self.pre + self.post
			self.num += 1
			return self.pre
		else:
			raise StopIteration

obj_1 = MyIterable(10) # 可迭代对象
obj_2 = MyIterator(10) # 迭代器

obj_1
>>> <__main__.MyIterable at 0x10ad36e90>
iter(obj_1) # 迭代器
>>> <__main__.MyIterator at 0x10addda90>
obj_2
>>> <__main__.MyIterator at 0x10ac56ad0>
iter(obj_2) # 返回自身
>>> <__main__.MyIterator at 0x10ac56ad0>

for i in obj_1:
	print i

for i in obj_2:
	print i

关于上述代码,这里讲几点:

  • 内建函数iter(),有两种用法,其中一种是iter(collection),作用是返回一个迭代器,本质上它是调用了collection的__iter__方法,并把__iter__的返回结果作为自己的返回值
  • 内建函数next(),本质上是调用了迭代器的next方法,并把next的返回结果作为自己的返回值
  • 当我们用for语句去访问一个可迭代对象时,实际上是先通过内建函数iter()获取了一个迭代器,然后再调用迭代器的next方法去获取值

可以通过isinstance来判断是一个对象是否属于可迭代对象或者迭代器:

from collections import Iterable, Iterator
isinstance([], Iterable)
>>> True
isinstance([], Iterator)
>>> False

在迭代可变对象时,一个序列的迭代器只是记录当前到达了序列中的第几个元素,所以如果在迭代过程中改变了序列的元素,更新会立即反应到所迭代的条目上:

c = [1, 2, 3, 4, 5]
for i in c:
	print i
	c.remove(i)
# 第一次迭代后,c=[2, 3, 4, 5]
>>> 1
# 迭代器取第2个元素,是3,迭代完成后,c=[2, 4, 5]
>>> 3
># 迭代器取第3个元素,是5,迭代完成后,c=[2, 4],迭代结束
>>> 5

  Python内部的数据结构,基本上都是可迭代对象,序列型的如字符串、列表和元组,非序列型的如字典和文件。

  使用迭代器,主要有以下优点:a).定义了统一的访问集合的接口,只要实现迭代器协议,就可以使用迭代器进行访问;b).惰性求值,在迭代至当前元素时才计算,省内存。有一点值得注意的是,迭代器不能多次迭代,不过,仅实现了__iter__方法的可迭代对象可以多次迭代。

3、生成器

  生成器是一种特殊的迭代器,它自动实现了迭代器协议(即__iter__next方法)。
  Python有两种不同的方式提供生成器:

  1. 生成器函数:常规函数定义,但是,使用yield表达式而不是return语句返回结果;yield表达式一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次从它离开的地方继续执行
  2. 生成器表达式:类似于列表推导式(构建于()内),但是,生成器表达式返回按需产生结果的一个对象,而不是一次构建一个结果列表

来看一个生成器函数的例子:

def gensquares(N):
	for i in range(N):
		yield i ** 2

obj = gensquares(5)
obj
>>> <generator object gensquares at 0x10add4640>

for item in obj:
	print item 

再看一个生成器表达式的例子:

# 生成器表达式定义于圆括号内
obj = (i ** 2 for i in range(5))
obj
>>> <generator object <genexpr> at 0x10abb78c0>

for item in obj:
	print item 

通过dir(obj)可以发现,上述两种方式生成的对象obj,均包含__iter__next方法。

下面这段代码,可以看出生成器函数内部的执行过程:

def zrange(n):
	print "beginning of zrange"
	i = 0
	while i < n:
		print "before yield", i
		yield i
		i += 1
		print "after yield", i
	print "endding of zrange"

obj = zrange(3)
print obj.next()
>>> beginning of zrange
>>> before yield 0
>>> 0
print obj.next()
>>> after yield 1
>>> before yield 1
>>> 1
print obj.next()
>>> after yield 2
>>> before yield 2
>>> 2
print obj.next()
>>> after yield 3
>>> endding of zrange
>>> ---------------------------------------------------------------------------
>>> StopIteration                             Traceback (most recent call last)
>>> <ipython-input-261-fb8e20feb398> in <module>()
>>> ----> 1 print obj.next()
>>> 
>>> StopIteration:

可以看出:

  • 当调用生成器函数的时候,函数只是返回了一个生成器对象,并没有执行
  • next方法第一次被调用的时候,生成器函数才开始执行,执行到yield处停止
  • next方法的返回值就是yield处的参数(yield somevalue)
  • 当继续调用next方法的时候,函数将接着上一次停止的yield处继续执行,并到下一个yield处停止;如果后面没有yield就抛出StopIteration异常。

  生成器还有两个重要的方法:

  1. send(value):我们已经知道,next方法可以恢复生成器状态并继续执行,其实send()是除next外另一个恢复生成器的方法;生成器函数中的yield表达式可以有一个值,而这个值就是send()方法的参数,所以send(None)等价于next,同样,next和send()的返回值都是yield处的参数(yield somevalue);有一点需要注意的是,调用send()传入非None值前,生成器必须处于挂起状态,否则将抛出异常,也就是说,第一次调用时,要使用next或send(None),因为没有yield来接收这个值。
  2. close():这个方法用于关闭生成器,对关闭的生成器再次调用next()或send()将抛出StopIteration异常

看一段代码:

def zrange(n):
	i = 0
	while i < n:
		val = yield i
		print "val is: ", val
		i += 1

obj = zrange(5)
print obj.next()
>>> 0
print obj.next()
>>> val is:  None
>>> 1
print obj.next("Hello")
>>> val is:  Hello
>>> 2
obj.close()
print obj.next()
>>> ---------------------------------------------------------------------------
>>> StopIteration                             Traceback (most recent call last)
>>> <ipython-input-29-ae1995e67165> in <module>()
>>> ----> 1 print obj.next()
>>> 
>>> StopIteration:

利用生成器的send()函数,可以实现在迭代过程中修改当前迭代值:

def zrange(n):
	i = 0
	while i < n:
		val = yield i
		i = i + 1 if val is None else val

obj = zrange(10)
print obj.next()
>>> 0
print obj.next()
>>> 1
print obj.send(5)
>>> 5

4、itertools

  Python内置的itertools模块包含了一系列用来产生不同类型迭代器的函数或类,它们的返回值都是迭代器,我们可以通过for循环来遍历取值,也可以使用next()来取值。
  itertools模块提供的迭代器函数或类有以下几种类型:

  1. 无限迭代器:生成一个无限序列,比如自然数序列 1, 2, 3, 4, …;
  2. 有限迭代器:接收一个或多个序列(sequence)作为参数,进行组合、分组和过滤等;
  3. 组合生成器:序列的排列、组合,求序列的笛卡儿积等;

  关于itertools的具体使用,可以参考这篇文章,这里不细述。


参考文献

[1] https://nvie.com/posts/iterators-vs-generators/
[2] https://foofish.net/iterators-vs-generators.html
[3] https://blog.csdn.net/Jmilk/article/details/52560255
[4] https://blog.csdn.net/jinixin/article/details/72232604
[5] https://www.zhihu.com/question/20829330
[6] https://www.jb51.net/article/73939.htm
[7] http://blog.51cto.com/11026142/1858860
[8] https://blog.csdn.net/u010159842/article/details/53896620
[9] http://python.jobbole.com/85240/
[10] http://funhacks.net/2017/02/13/itertools/
以上为本文的全部参考文献,对原作者表示感谢。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Python迭代器 的相关文章

  • 为什么 matplotlib 底图没有绘制地图中某些区域的颜色?

    下面的代码应该为越南的所有州着色 import pandas as pd import matplotlib pyplot as plt from mpl toolkits basemap import Basemap fig ax plt
  • matplotlib:调整图形窗口大小而不缩放图形内容

    当您调整图形大小时 Matplotlib 会自动缩放图形窗口中的所有内容 通常这是用户想要的 但我经常想增加窗口的大小 为其他东西腾出更多空间 在这种情况下 我希望在更改窗口大小时预先存在的内容保持相同的大小 有谁知道一个干净的方法来做到这
  • 根据另一个数据框中找到的范围填充数据框中的列

    我试图根据该记录的索引值是否落在另一个数据框中的两列定义的范围内来填充数据框中的列 df1 看起来像 a 0 4 1 45 2 7 3 5 4 48 5 44 6 22 7 89 8 45 9 44 10 23 df2 是 START ST
  • Pygame 让精灵按照给定的旋转行走

    很久以前我做了一个Scratch脚本 我想用Pygame将其转换为Python 有很多示例显示图像的旋转 但我想知道如何更改精灵的旋转以使其沿给定方向移动 而不更改图像 这是我的暂存代码 这是我的 Pygame 精灵类 class Star
  • 在 Python 中使用类作为命名空间是个好主意吗

    我正在将一堆相关的东西放入一个类中 主要目的是将它们组织到命名空间中 class Direction north 0 east 1 south 2 west 3 staticmethod def turn right d return tu
  • 监控培训课程如何运作?

    我试图理解使用之间的区别tf Session and tf train MonitoredTrainingSession 以及我可能更喜欢其中之一 似乎当我使用后者时 我可以避免许多 杂务 例如初始化变量 启动队列运行程序或设置文件编写器以
  • 如何在每次运行 python 程序时添加新列

    我希望我的表的第一列作为卷号 第二列作为名称 每当我运行 python 程序时 我想在表中添加一列日期 在这个新列中 我想填充从 user list 获得的列表将包含值 P A P P 等 如何处理 我尝试首先通过 alter 命令添加一列
  • 检查列表是否已排序的 Pythonic 方法

    有没有一种Python式的方法来检查列表是否已经排序ASC or DESC listtimestamps 1 2 3 5 6 7 就像是isttimestamps isSorted 返回True or False 我想输入一些消息的时间戳列
  • 如何不断地将 STDOUT 发送到我的 python TCP 服务器?

    我有简单的 python echo 服务器 它使用套接字 并向客户端回显随机数 我有另一个程序 每 2 秒将值打印到标准输出 如果它只是一个脚本 我可以像这样重定向 stdout python script py 并像这样在脚本中获取它da
  • Python:绘制甘特图的模块

    有没有一个好的Python绘图模块甘特图 http en wikipedia org wiki Gantt chart 我试过了开罗情节 http linil wordpress com 2008 09 16 cairoplot 11 但它
  • telethon 库:如何通过电话号码添加用户

    我正在研究 Telegram 的 Telethon 库 它可以使用 Telegram API 充当 Telegram 客户端 重要提示 这是电报客户端 API https core telegram org telegram api 而不是
  • Django - 使 ModelForm(ImageField 的)仅接受某些类型的图像

    我将 Pillow 2 3 0 与 Django 一起使用 并且在 models py 中有一个 ImageField 如下所示 class UserImages models Model user models ForeignKey Us
  • 如何在python中访问矩阵每个元素的相邻单元格?

    这里 如果两个单元共享边界 则它们被认为是相邻的 例如 A 5 6 4 2 1 3 7 9 8 这里 索引 0 0 的相邻元素位于索引 0 1 和 1 0 处 索引 1 1 的相邻元素位于索引 0 1 1 0 2 1 处 和 1 2 假设你
  • 使用 conda 安装额外功能

    With pip我们可以使用方括号安装子包 例如与阿帕奇气流 https pythonhosted org airflow installation html pip install airflow all 有类似的东西吗conda或者我必
  • Pandas 中的数据透视表小计

    我有以下数据 Employee Account Currency Amount Location Test 2 Basic USD 3000 Airport Test 2 Net USD 2000 Airport Test 1 Basic
  • Matplotlib:检查空图

    我有一个循环加载并绘制一些数据 如下所示 import os import numpy as np import matplotlib pyplot as plt for filename in filenames plt figure i
  • 如何正确将 tflite_graph.pb 转换为 detector.tflite

    我正在使用tensorflow对象检测API使用tensorflow中的ssdlite mobilenet v2 coco 2018 05 09来训练自定义模型模型动物园 https github com tensorflow models
  • scikit-learn kmeans 聚类的初始质心

    如果我已经有一个可以作为初始质心的 numpy 数组 我该如何正确初始化 kmeans 算法 我正在使用 scikit learn Kmeans 类 这个帖子 具有选定初始中心的 k 均值 https stackoverflow com q
  • 具有重复值的 Sqlite 列

    就说专栏吧aSQLite 数据库的非常重复 始终有相同的 4 个值 其他值可能稍后出现 但不同值的数量将少于 1000 个 VALUES hello world it s a shame to store this str many tim
  • 使用 python/scipy 进行 voronoi 和 lloyd 松弛

    如何使用 Qhull 确定哪些 voronoi 单元 按索引 是 正确的 由 现有顶点 组成 我正在尝试使用 LLoyds 算法和 scipy spatial Voronoi 它是 Qhull 的包装器 生成的输入来执行约束松弛 就代码而言

随机推荐

  • LeetCode146. LRU缓存机制

    题目来源 https leetcode cn com problems lru cache submissions 题目描述 代码如下 方式一 LRU实现手段就是哈希表 链表 而LinkedHashMap正好存在该特性 因此利用Linked
  • 符号表

    符号表 每一个符号表对象都提供getAt函数来获取指定名称记录 Acad ErrorStatus getAt const char pEntryName 需要获取对象的名称 AcDb OpenMode mode 打开方式 XXXTableR
  • linux MongoDB 安装与配置

    目录 MongoDB概述 MongoDb安装部署 MongoDB设置密码 MongoDB操作命令与说明 配置文件说明 备份操作 还原操作 MongoDB实际场景应用 MongoDB概述 MongoDB是一个非关系型数据库管理系统 它使用文档
  • python注意事项(一)

    1 Python中input接收到的是字符串类型 2 i j k 的表达形式 print s s s i j k ctrl s 是保存的功能 4 在print中加上end 结束换行 例如print i end print 可以实现换行效果
  • 求1-1/2+1/3-1/4+....+1/100的和 (C语言)

    代码 include
  • linux的汇编指令大全,Linux 汇编指令集

    注意 汇编语言下操作所有的数据必须取出数据放到通用寄存器中处理 完毕之后从通用寄存器中读取出放到指定的寄存器中 1 汇编程序书写格式 声明程序段时不要顶格写 如下 AREA test CODE READONLY 声明代码段 段名 类型 属性
  • C#之static的用法详解

    有的东西你天天在用 但未必就代表你真正了解它 正如我之前所了解的static 一 静态类 静态类与非静态类的重要区别在于静态类不能实例化 也就是说 不能使用 new 关键字创建静态类类型的变量 在声明一个类时使用static关键字 具有两个
  • linux——递归遍历目录的实现

    一 递归目录分析 2 实现 include
  • ehcache springboot_SpringBoot入门建站全系列(十三)本地缓存的使用(Ehcache和caffeine的使用)

    SpringBoot入门建站全系列 十三 本地缓存的使用 Ehcache和caffeine的使用 一 概述 本地缓存 就是使用应用内使用本地内存将数据暂缓存储 一般数据库的查询如果不怎么改动 可以用本地缓存暂存 远程缓存 比如redis 就
  • (支付接口)签名验签和请求参数加密方法

    当我们在测试支付接口的时候 发现每个接口需要将请求的敏感数据加密后才能请求接口 遇到这种问题怎么解决呢 特别是当我们在写python接口自动化测试脚本的时候 这是必须要解决的问题 下面我来详细的写下这个过程 首先 我们需要拿到商户的公钥和公
  • Flex学习笔记2——第一个Flex程序

    Flex的调试 1 使用trace 相当于alert 2 使用firefox调试 下载debug版本的firefox flash player插件 下载插件HttpFox FlashTracer Cache Status 三个插件 Flas
  • 使用Frida对Windows平台的程序进行逆向分析(转)

    https developer 51cto com art 202009 626689 htm
  • 【深度学习基本概念】上采样、下采样、卷积、池化

    上采样 概念 上采样 upsampling 又名放大图像 图像插值 主要目的是放大原图像 从而可以显示在更高分辨率的显示设备上 上采样有3种常见的方法 双线性插值 bilinear 反卷积 Transposed Convolution 反池
  • 海森矩阵及其应用

    海森矩阵及其应用 转载 2017年04月20日 09 59 48 标签 梯度下降算法 微积分 牛顿迭代法 原文参考链接 here 原文讲得到很详细 海森矩阵 在数学中 海森矩阵 Hessian matrix或Hessian 是一个自变量为向
  • 【无人机 学习笔记 2】无人机导航制导与控制

    梳理完控制以及飞行力学的一些基本原理之后 就该正式进入无人机导航 制导与控制的讨论了 导航制导与控制是无人机系统中最复杂的分系统 其功能可以有多种划分方法 本文中 我们就以下面框图所示的划分方法为例 对无人机导航制导与控制系统的基本原理和常
  • numpy 查找元素位置 numpy.where

    numpy where condition x y 详细用法请大家详见官方文档 这里举几个例子 import numpy as np a np array 1 2 3 4 5 print np where a lt 3 查找小于3的元素的位
  • Python学习笔记(CSDN)- 1 - Python简介

    按照CSDN上的课程进行学习 Python简介 预备知识 CSDNPython入门技能树 先是附上这门课的思维导图 目录 1 Python定义 2 Python发展史 2 1 Python的发展 2 2 Python的版本更新 2 3 Py
  • JavaScript基础:Iterator概念及用法

    一 由来及意义 Javascript中表示 集合 的数据结构 主要是 Array Object Map Set 这四种数据集合 除此之外 它们相互之间还可以组合使用 例如Array的成员是Map Map的成员是Object等 因此Javas
  • 一分钟学会gitee上传文件到仓库

    在网上Gitee准备 1 1 注册登录 gitee 1 2 在gitee上创建仓库 点击gitee头像旁边的 选择 新建仓库 1 3 开始新建 然后点击 创建 仓库名称 最好都用英文 这里方便讲解就不勾选后面的内容了 接着直接点击 创建 就
  • Python迭代器

    迭代器是在Python2 2中被加入的 它为类序列对象提供了一个类序列的接口 有了迭代器可以迭代一个不是序列的对象 因为它表现出了序列的行为 关于Python中的迭代器 有几个比较容易混淆的概念 可迭代对象 iterable 迭代器 ite