Python中的迭代器与生成器使用及说明
作者:Lareges 发布时间:2022-01-01 08:14:27
一、迭代器(Iterator)
1.1 可迭代对象(Iterable)
可迭代对象,可以简单理解为可遍历对象,即能够使用 for 循环遍历的对象。Python中常见的可迭代对象有:列表、元组、字符串、集合、range、字典等。
迭代器和生成器都是可迭代对象。
对于Python中的任意对象,只要它定义了可以返回一个迭代器的 __iter__ 方法,或者定义了可以支持下标索引的 __getitem__ 方法,那么它就是一个可迭代对象。
对可迭代对象使用 __iter__ 方法后,会返回一个迭代器。
如何判断一个对象是否为可迭代对象呢?请看下例。
from collections.abc import Iterable
isinstance([1, 2, 3], Iterable) # True
isinstance((1, 2, 3), Iterable) # True
isinstance('123', Iterable) # True
isinstance({1, 2, 3}, Iterable) # True
isinstance(range(3), Iterable) # True
isinstance({'key': 'value'}, Iterable) # True
isinstance(123, Iterable) # False
可以看出,我们只需要使用 isinstance(object, Iterable) 即可判断给定的 object 是否为可迭代对象。
严格来讲,isinstance() 只会将有 __iter__ 方法的对象判断为 Iterable。
换言之,仅用 __getitem__ 方法实现的可迭代对象会被 isinstance() 误判为不可迭代对象。
最正确的做法是直接尝试 iter(object),如果没有报错,则说明 object 是可迭代对象。
1.2 将可迭代对象转化为迭代器
我们可以将现有的可迭代对象转化为可迭代器:
s = '12345'
myiter = iter(s)
myiter
# <str_iterator at 0x25e6f40d130>
不断调用 next 方法来依次获取迭代器的元素:
next(myiter)
# '1'
next(myiter)
# '2'
next(myiter)
# '3'
next(myiter)
# '4'
next(myiter)
# '5'
next(myiter)
# StopIteration:
可见迭代器执行到最后时会抛出一个 StopIteration 异常。
为避免这种异常,我们完全可以用更简单的 for 循环去遍历:
for e in myiter:
print(e)
# 1
# 2
# 3
# 4
# 5
1.3 构造迭代器
构造一个迭代器只需要在自定义的类中实现两个方法:__iter__ 和 __next__ 。
迭代器是一个可以记住遍历位置的对象。
迭代器对象会从第一个元素开始访问,直到所有元素都被访问为止,且只能前进不能后退。
当我们构造类时,必须要有一个名为 __init__() 的函数,该函数可以在实例化时进行一些初始化。
__iter__()
方法的行为类似,可以执行操作(初始化等),但必须始终返回迭代器对象本身。__next__()
方法还允许你进行其他操作,并且必须返回序列中的下一项。
class MyIter:
def __iter__(self):
self.count = 1
return self
def __next__(self):
x = self.count
self.count += 1
return x
我们创建了一个返回数字的迭代器,每次序列的数值都将 +1。
myiter = iter(MyIter())
next(myiter)
# 1
next(myiter)
# 2
next(myiter)
# 3
如果我们一直调用 next() 的方法,则序列的值将会无限递增下去。即如果我们使用 for 循环去遍历上述迭代器,循环将永远进行下去…
myiter = iter(MyIter())
for e in myiter:
print(e)
# 循环将一直进行下去...
为了防止迭代永远进行下去,我们可以在迭代次数达到一定值时抛出 StopIteration 异常。
class MyIter:
def __iter__(self):
self.count = 1
return self
def __next__(self):
if self.count <= 5:
x = self.count
self.count += 1
return x
else:
raise StopIteration
这样再执行 for 循环就不会一直进行下去了:
myiter = iter(MyIter())
for e in myiter:
print(e)
# 1
# 2
# 3
# 4
# 5
二、生成器(Generator)
在Python中,一边迭代(循环)一边计算的机制,称为生成器。生成器能够迭代的关键是因为它有一个 __next__ 方法。
为什么要有生成器呢?我们知道,列表中的所有数据都存储在内存中,如果有海量数据的话将会非常消耗内存。很多时候,我们只需要访问列表中前面的元素,这样一来后面的元素所占用的空间就白白浪费了。
如果列表元素能够按照某种算法推算出来,那我们就可以在循环的过程中不断推算出后续的元素,这样就不必创建完整的列表,从而节省了大量的空间(即用多少就生成多少)。
有以下两种常用方法来创建生成器:
将列表解析式中的 [] 改为 ()。在自定义的函数中使用 yield 关键字。此时这个函数就不再是一个普通函数,而是一个生成器,调用该函数就是创建了一个生成器对象。
2.1 使用 () 构造生成器
比较以下两段代码:
a = [x for x in range(3)]
type(a)
# list
a = (x for x in range(3))
type(a)
# generator
我们还可以比较列表解析式和生成器的耗时:
tic = time.time()
a = sum([x for x in range(10000000)])
toc = time.time()
print(toc - tic)
# 0.9081981182098389
tic = time.time()
a = sum((x for x in range(10000000)))
toc = time.time()
print(toc - tic)
# 0.6906485557556152
我们当然可以对生成器使用 next() 方法:
next(a)
# 0
next(a)
# 1
next(a)
# 2
next(a)
# StopIteration:
但一般我们不会用 next() 来获取下一个返回值,而是直接使用 for 循环来迭代。
2.2 使用带有 yield 关键字的函数构造生成器
带有 yield 的函数不再是一个普通函数,而是一个生成器。
yield 相当于return一个值,并且记住这个返回的位置,下次迭代时,代码从 yield 的下一条语句开始执行。
我们可以通过下面的例子先来理解一下:
def num():
print('开始执行')
for i in range(5):
yield i
print('继续执行')
mygen = num()
type(mygen)
# generator
由此,我们成功创建了一个生成器对象。接下来调用 next 方法观察这个生成器是如何工作的:
next(mygen)
# 开始执行
# 0
next(mygen)
# 继续执行
# 1
next(mygen)
# 继续执行
# 2
next(mygen)
# 继续执行
# 3
next(mygen)
# 继续执行
# 4
next(mygen)
# StopIteration:
当然我们也可以使用 for 循环来遍历这个生成器:
for step in mygen:
print(step)
# 开始执行
# 0
# 继续执行
# 1
# 继续执行
# 2
# 继续执行
# 3
# 继续执行
# 4
# 继续执行
来源:https://raelum.blog.csdn.net/article/details/124573424


猜你喜欢
- 用Go语言实现登录验证,有3次机会,如果用户名为 zhangsan ,密码为 123456 ,则提示登录成功,否则提示还有几次机会,次数用完
- Base64编码Base64编码将二进制数据转换为文本格式,通过通信通道传递,用户可以安全地处理文本. Base64也称为隐私增强电子邮件(
- 实例如下所示:# -*-coding:utf-8-*-import osfile_obj = open("test2.txt&qu
- 本文实例讲述了Python实现螺旋矩阵的填充算法。分享给大家供大家参考,具体如下:afanty的分析:关于矩阵(二维数组)填充问题自己动手推
- 首先抛出我们在讨论使用回调编程时的一些观点:激活errback是非常重要的。由于errback的功能与except块相同,因此用户需要确保它
- 关于算法的学习,小编觉得编程语言中的算法大都有一些相通的地方,主要的方面一是了解这一算法能用来干什么,另一方面,学习它在这类编程语言中怎么实
- py2exe在sourceforge 的下载只支持到2.7。针对python3.0+的版本,需要自己编译。1.下载源码svn checkou
- 网上的SQL优化的文章实在是很多,说实在的,我也曾经到处找这样的文章,什么不要使用IN了,什么OR了,什么AND了,很多很多,还有很多人拿出
- 如何制作一个搜索引擎链接程序?多收集几个网站的,然后我们引用它到自己的页面中。接下来,我们要创建页面用于搜索:<center>&
- 在python中gui编程有很多中选择,如果是相对简单的gui的话使用python自带的tkinter即可,但是由于tkinter没有详细的
- 阅读上一篇:javascript面向对象编程(一)[javascript模拟传统OOP]javascript是一种非常灵活的语言,它的灵活度
- 抽象工厂模式抽象工厂模式是一种创建型设计模式, 它能创建一系列相关的对象, 而无需指定其具体类。抽象工厂定义了用于创建不同产品的接口, 但将
- 本文介绍了asp编程中使用数组的各种方法,并给出了详细的asp实例代码方便大家理解。asp中数组的定义Dim MyArray My
- 一、Vue3 与 Vue2 区别详述1. 生命周期对于生命周期来说,整体上变化不大,只是大部分生命周期钩子名称上 + “
- 本意是为了和手写jdbc对照,不过不要和原来的手写连接重名。打开cmd,直接输入notepad就打开了记事本。jdk1.5之后不必配置cla
- 一、项目工程目录:二、具体工程文件代码:1、新建一个包名:common(用于存放基本函数封装)(1)在common包下新建一个base.py
- 一、zipfile模块的简述zipfile是python里用来做zip格式编码的压缩和解压缩的,由于是很常见的zip格式,所以这个模块使用频
- 目录与SpringBoot2.0整合 1、核心依赖2、配置文件3、实体类对象4、JPA框架的用法5、封装一个服务层逻辑测试代码块&
- 如何查看MySQL初始密码问题在安装MySQL过程中,以管理员身份运行cmd后进入MySQL的bin目录,然后输入命令“
- 本文是对《Python Qt GUI快速编程》的第9章的堆叠窗口例子Vehicle Rental用Python3+PyQt5+Qt Desi