可迭代对象、迭代器、生成器以及yield关键字使用

第二十七天:Python迭代器与生成器 本文详细介绍了Python中的可迭代对象与迭代器协议。可迭代对象(Iterable)指能用for循环遍历的对象,如列表、元组、字典等,它们实现了__iter__()方法。迭代器则通过__iter__()和__next__()方法实现,能保持迭代状态,但只能遍历一次。文章对比了两者的区别,并展示了如何验证对象类型。此外,还讲解了自定义迭代器的实现方法,包括状态保持机制和分离可迭代对象与迭代器的最佳实践,通过代码示例说明了如何创建支持多次迭代的类。 阅读详情

1.可迭代对象(iterable object)

能使用for遍历的对象,都是可迭代对象,例如:list、set、dict、str、tuple等容器都属于可迭代对象。可迭代对象必然实现了__iter__()函数,换言之,一个对象实现了__iter__()函数,就是可迭代对象。

2.迭代器(iterator)

一个对象同时实现了__iter__()和__next__()函数,称之为迭代器。可迭代对象不一定是迭代器,迭代器一定是可迭代对象。
iter()函数的作用是返回一个迭代器对象,next()函数作用是返回迭代器的下一个值。

3.生成器(generator)

能够一边迭代一边计算并产生值的对象,称为生成器。生成器仅仅保存了一套生成数值的算法,不是让算法一次性生成所有值,而是在调用它时才计算产生一个新的值并返回。将一个对象变为生成器的方法是在函数内部使用yield关键字。生成器是一种特殊的迭代器,迭代器只能够返回值,而生成器不光可以返回值,还能接收值(send函数)。
为什么要使用生成器?假设现在需要读取0,2,4,6,8··· ···等一亿个数字存到list中,但每次只会使用1个值,如果一次性读取全部数字到内存中将非常消耗内存。因为这些数字是有规则的,它们都是偶数,所以可以按照规则每次生成一个值使用,用完便释放内存,这样内存中只会消耗一个数字的存储空间。简单说就是:如果我想使用的海量数据存在规则,如果想节省内存,便可以使用生成器。

3.1 创建生成器

1.使用列表生成式创建生成器:
下面是使用列表生成式创建一个列表的方法,得到的类型是list:

test_list = [i*2 for i in range(10)]
print(type(test_list))
for i in test_list:
    print(i)

在这里插入图片描述
只需要把列表生成式的中括号改为小括号,便可以得到一个生成器对象:

test_list = (i*2 for i in range(10))
print(type(test_list))
for i in test_list:
    print(i)

在这里插入图片描述
2.一个函数中使用了yield关键字,调用函数时就生成了一个生成器。

3.2 yield关键字

下面来看一下yield的使用方式和作用。
首先先看下面这段函数:

def test_generator():  # 0.定义函数
    print("generator start")
    for i in range(2):
        yield i  # 1.yield关键字将函数暂停,并抛出i
        print(f"i is {i}")
gen = test_generator()  # 3.调用函数,获得一个生成器
print(next(gen))  # 4.调用next(),获取下一个值
# print(gen.send('msg'))
print(next(gen))
print(next(gen))

解释一下这段代码的含义:
1.第0处,定义了一个函数test_generator(),因为函数内部使用了yield关键字,所以这不再是一个普通函数,当调用它时函数内的代码并不会执行,而是返回一个迭代器对象。
2.关于yield应该怎样理解?很多人建议当做return理解,我觉得yield=pause+raise(抛出)更容易理解,并且此处的raise并不会终止函数,仅仅抛出一个值而已,且抛出后值便不存在了。当程序执行到yield时,会先将程序暂停,然后将yield后的值抛给调用方。
3.next()每次调用将返回迭代器的下一个值,相当于每次执行时都会让原先暂停的程序继续执行,直到遇见下一次yield。

执行结果如下:
在这里插入图片描述

下面看一下程序的执行顺序:
1.第1次调用next()函数时,程序开始执行,打印了generator start。然后test_generator()函数到达第一个含有yield的语句:yield i,此时程序暂停,将yield后的i抛出,即将i=0返回,并将0打印出来;
2.第2次调用next()函数,程序从yield i后面执行,打印出i is 0,然后i=1,程序又执行到yield i处,再次将i抛出(此时i=1),然后打印1;
3.第3次调用next(),程序从上次yield处继续执行,打印出第二个i is 1;此时由于遍历完,生成器结束,抛出停止迭代异常(StopIteration)。关于此异常,可以了解一下for循环的执行原理。

再看一下下面这段代码,不同之处是1处的res = yield。

def test_generator():  # 0.定义函数
    print("generator start")
    for i in range(2):
        res = yield i  # 1.yield关键字将函数暂停,并抛出i
        print(f"res:{res}")

gen = test_generator()  # 3.调用函数,获得一个生成器
print(next(gen))  # 4.调用next(),获取下一个值
print(next(gen))
print(next(gen))

执行结果如下:
在这里插入图片描述

下面看一下程序的执行顺序:
1.第1次调用next()函数时,程序开始执行,打印了generator start。然后test_generator()函数到达第一个含有yield的语句:res = yield i,此时程序暂停,将yield后的i抛出,即将i=0返回,并将0打印出来。需要注意的是,程序暂停的位置是yield,此时i=0并没有赋给res;
2.第2次调用next()函数,程序从res = yield i继续执行,由于上一步并没有将i=0赋值给res就抛出了,所以此时首先应该执行的是赋值语句,但是由于i已经被抛出,此时已经没有i=0了,res会被赋值为None,便打印出了第一个res:None,然后i=1,程序又执行到res = yield i处,再次将i抛出(此时i=1),然后打印1;
3.第3次调用next(),程序从上次yield处继续执行,同样由于i=1已经被抛出,res被赋值为None,打印出第二个res:None;此时由于遍历完,生成器结束,抛出停止迭代异常(StopIteration)。关于此异常,可以了解一下for循环的执行原理。

总结一下就是:当执行到yield关键字时,程序会先暂停,并将yield后面的值抛出去(抛出去本地也就没有了),调用方会接到抛出的值,调用next会让程序继续执行直到再次遇到yield暂停。

那么问题来了,如果我想让下次执行时res可以被赋值怎么办?send()函数派上用场。

3.3 send()函数的使用

send()函数和next()函数是类似的,只不过在next()的基础上增加了传参功能。执行send()函数相当于执行了next() + 传参两个行为。

def test_generator():  # 0.定义函数
    print("generator start")
    for i in range(2):
        res = yield i  # 1.yield关键字将函数暂停,并返回i
        print(f"res:{res}")

gen = test_generator()  # 3.调用函数,获得一个生成器
print(next(gen))  # 4.调用next(),获取下一个值
print(gen.send('msg'))
print(next(gen))

结果:
在这里插入图片描述
注意,我将第二个print(next(gen))变成了print(gen.send('msg')),执行到这里的时候,就不单纯是执行next(gen)了,而是先将yield i替换为'msg',即将'msg'赋值给res,然后继续执行。

总结一下,next()和send()的不同是send()会传入一个参数替代yield xxx。

3.4 使用yield实现协程:生产者-消费者模型

def consumer():
    print("first...")
    res = 'default'
    while True:
        product = yield res
        # if not product:
        #     return
        print(f"consuming {product}")
        res = f"{product} is finished..."

def produce(c):
    ret = c.send(None)  # 开始迭代第一次,等同于执行next(c)
    print(ret)
    product = 0
    while product < 3:
        product += 1
        print(f"producing {product}")
        r = c.send(product)
        print(f"Consumer return:{r}")
    c.close()

c = consumer()
produce(c)

每当生产者生产一个产品,就切换到消费者去消费,消费完成后再切换到生产者继续生产…这便是用户级线程——协程的一个经典实现。以上代码的执行结果如下:
在这里插入图片描述
1.消费者是一个生成器,生产者首先通过c.send(None)使迭代器开始执行,此处也可以使用next(c)。消费者首先打印first...,当消费者执行到product = yield res时,程序暂停,并将res = 'default'抛出,由ret接收到抛出的'default'值,打印出default;
2.生产者生产出1号产品后,打印producing 1,然后调用send(product)函数使消费者生成器继续执行,由于send(product)携带了product 1,所以相当于把yield res替换为了product 1,先对product进行赋值,即:product = product 1,然后继续执行,打印出consuming 1,并给res赋值为1 is finished...,程序继续执行循环,到yield再次暂停,将res = '1 is finished...'抛出;
3.生产者接收到res,将其打印出来。然后生产出2号产品,打印出producing 2,然后调用send(product),生成器接收到后将其赋值给product,并打印consuming 2,并给res赋值为2 is finished...,程序到yield再次暂停,将res = '2 is finished...'抛出;
4.生产者接收到res,将其打印出来。然后生产出3号产品,打印出producing 3,然后调用send(product),生成器接收到后将其赋值给product,并打印consuming 3,并给res赋值为3 is finished...,程序到yield再次暂停,将res = '3 is finished...'抛出;
3.生产者接收到res,将其打印出来。此时生产者跳出循环,调用close()函数关闭消费者生成器,结束程序。

3.5 使用生成器多次读取固定大小的文件内容

读取文件时,如果一次性读取大文件会占用大量内存,如果使用生成器每次读取固定大小的文件块便可以避免这种问题。

def read_file(file_path):
    BLOCK_SIZE = 2**3
    with open(file_path, 'r') as fs:  # 带'b'模式是按字节读取,默认是按字符
        while True:
            block = fs.read(BLOCK_SIZE)
            if block:
                yield block
            else:
                return

rf = read_file('./test.txt')  # 创建生成器对象
# print(next(rf))
for i in rf:
    print(i)
test_generator.py python 官方开发的generator 的测试文件, 有很多有用的知识 立即下载

相关推荐

Anolis OS 23实战:5分钟搞定AI环境配置(含NVIDIA驱动一键安装)

本文详细介绍了如何在Anolis OS 23上快速配置AI开发环境,包括NVIDIA驱动的一键安装和TensorFlow、PyTorch等AI框架的部署。通过智能硬件适配引擎和优化后的二进制包,用户可在5分钟内完成传统需要数小时的配置工作,显著提升AI项目开发效率。

weixin_30335575的博客 535

pytest自动生成测试类 demo

pytest自动生成测试类 demo

喵酱 2206

CAN总线错误帧全解析:从数据包到硬件层的故障树分析

本文深入解析CAN总线错误帧的故障树分析方法,从数据包到硬件层全面剖析错误机制。通过物理层信号测试、数据链路层错误解码和应用层协议排查,提供系统化的诊断框架。结合CANoe配置和示波器波形分析,帮助工程师快速定位总线故障,提升系统可靠性。

weixin_26742753的博客 488

c++Iterators(迭代器)和auto关键字

迭代器用于通过“指向”来访问和迭代数据结构(向量、集合等)的元素。我个人认为迭代器是循环的一种特殊情况,迭代器由于参数特殊性,更偏向于遍历整个数据结构,但我们循环中不仅仅用于遍历结构,还有时候是用于筛查一些特殊值或者进行一些特殊的处理。

m0_74078963的博客 567

【Latex】【附安装文件】Miktex和TexStudio的安装与配置

这代表“构建并运行”,一般来说,我们只需要点这个按钮来运行Latex代码。保存一下代码文件,再运行一次,PDF文件会出现在与代码文件相同的文件夹下。安装完成后,你可能会跳转到一个页面中,你看到这个页面就意味着安装成功了。其实不需要你敲,你只要将下面的代码复制粘贴到你刚刚的文件中即可。成功安装MikTex后,我们需要进行更新,减少后续使用的麻烦。如果懂得将安装路径设置在你想要的盘中,你可以自己设置。双击上文的.exe文件后,你会看到如下画面。如果不懂,为避免不必要的麻烦,请直接点击“

未来就在脚下。 1万+

EtherCAT主站视角:如何正确配置一个支持动态PDO的从站(以倍福TwinCAT为例)

本文详细解析了在TwinCAT环境下配置支持动态PDO的EtherCAT从站的全过程。通过MDP(Modular Device Profile)实现模块化配置,涵盖从站工作原理、TwinCAT工程配置、调试技巧到高级应用实践,帮助工程师快速掌握动态PDO配置技术,提升工业现场设备扩展与维护效率。

weixin_30466039的博客 266

C# 中的迭代器(iterator)和 yield 关键字有什么作用?

在 C# 中,迭代器(iterator)和yield关键字主要用于创建自定义的枚举器(enumerator),使得可以轻松地遍历集合中的元素。

weixin_45422672的博客 277

AS5600磁编码器IIC通信与角度读取实战指南

本文提供了AS5600磁编码器通过IIC通信协议实现高精度角度读取的完整实战指南。详细解析了硬件连接要点、IIC通信时序、Arduino与STM32平台驱动代码,并分享了多圈测量、噪声滤波等进阶应用与避坑技巧,帮助开发者快速将这款非接触式角度传感器集成到电机控制、旋钮等项目中。

weixin_29172201的博客 242

python3自学之路-笔记7

Python3 迭代器与生成器 迭代器有两个基本的方法:iter()和next()。 字符串,列表或元组对象都可用于创建迭代器: a=['a','b','c','d','e'] b=iter(a) for i in range(len(a)): print(next(b)) 这段代码中b的类型为&lt;class 'list_iterator'&gt; 生成器 在 ...

qq_32394351的博客 6187

STM32掉电保存配置参数技巧

本文介绍了使用STM32内置备份寄存器(BKP)实现掉电保存配置参数的方法。相比外接EEPROM或Flash,备份寄存器无需外设但存储容量有限,需通过VBAT引脚连接纽扣电池供电。文章详细说明了初始化步骤,包括使能PWR时钟、允许访问备份寄存器等操作,并提供了RTC初始化的代码示例。同时介绍了HAL库中读写备份寄存器的函数使用方法,包括HAL_RTCEx_BKUPWrite和HAL_RTCEx_BKUPRead函数的具体参数和调用示例。该方法适用于需要小容量数据掉电保存的应用场景。

hanhanhan233的博客 946

别再被网站识别成机器人了!用Python的undetected_chromedriver+Selenium实现无痕自动化

本文详细介绍了如何利用Python的undetected_chromedriver和Selenium实现无痕自动化,突破现代网站的反爬系统。通过修正浏览器指纹、模拟人类操作行为,并结合实战案例,帮助开发者构建难以被识别的自动化脚本,适用于电商监控等场景。

weixin_30367169的博客 251

Python 中yield 以及生成器和迭代器

迭代 当你建立了一个列表,你可以逐个地访问这个列表的元素,而这个访问的过程叫做迭代(iteration) 凡是能使用for…in…语句的对象,都叫做可迭代对象,例如:list、string、文件等等 这些可迭代对象非常方便,因为你可以根据自己的需要来访问它们。但是同时也需要将所有的值存入内存当中,无论你是不是需要所有的值,可能对于一个列表[x for x in range(100000)],你仅仅...

定格空间 358

从ROS2到ROS1:Lightning-LM激光SLAM模块的移植实践与核心代码解析

本文详细解析了将高性能激光SLAM算法Lightning-LM从ROS2移植到ROS1的完整实践过程。针对ROS1与ROS2在节点初始化、话题通信、参数服务器及TF发布等核心API的差异,文章提供了loc_system和slam模块的核心代码改造示例,并给出了适配catkin的CMakeLists.txt与package.xml配置方案,帮助开发者在现有ROS1生态中高效集成先进的SLAM模块。

weixin_29224529的博客 172

生成器、迭代器、可迭代对象三者的联系和区别

生成器、迭代器、可迭代对象的联系和区别可迭代对象定义判断迭代器概念判断生成器概念yield关键字实例①:实例②:yield生成器实现斐波那契数列实例③:send()方法修改生成器的状态三者的联系与区别: 可迭代对象 定义 凡是实现了__iter__方法的对象都称之为可迭代对象,常见的有: 容器都是可迭代对象 1.list 2.set 3.dict 4.str 5.tuple 其他的: 6.打开状态的files 7.打开状态的sockets等 还有一种说法是,实现了__getitem__方法且__getite

sfakh的博客 2641

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

Python 可迭代对象、迭代器、生成器的详解

Python 可迭代对象、迭代器、生成器的详解 一、判断可迭代对象、迭代器: 使用模块from collections import Iterable, Iterator,可以检测该对象是可迭代对象还是迭代器。 Iterable: 检查是否可迭代对象 Iterator:检查是否迭代器 举个例子,判断可迭代对象(Iterable): from collections import Iterable...

Fe_cow的博客 1250

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

迭代器与生成器

let i = 0return {return {/*** 第一次* 第二次* ……* 第十次* 第十一次*/

weixin_43063398的博客 1689

html生成器_可迭代对象、迭代器和生成器(1)

简单来说可迭代对象包含迭代器,迭代器包含生成器。可迭代对象和迭代器迭代是重复反馈过程的活动,其目的通常是为了逼近所需目标或结果。每一次对过程的重复称为一次“迭代”,而每一次迭代得到的结果会作为下一次迭代的初始值。在python中,可迭代的表象是对list、tuple、dict、set、str、range对象等类型的数据使用for循环依次从其中拿到数据进行使用,这个过程称做遍历,也叫迭代。代码角度看...

weixin_39637545的博客 237

一文读懂Python可迭代对象、迭代器和生成器

原文地址:一文读懂Python可迭代对象、迭代器和生成器 我们都知道,序列可以迭代。但是,你知道为什么吗? 本文来探讨一下迭代背后的原理。 序列可以迭代的原因:iter 函数。解释器需要迭代对象 x 时,会自动调用 iter(x)。内置的 iter 函数有以下作用: (1) 检查对象是否实现了iter方法,如果实现了就调用它,获取一个迭代器。 (2) 如果没有实现iter方法,但是实现了getitem方法,而且其参数是从零开始的索引,Python 会创建一个迭代器,尝试按顺序(从索引 ...

piglite的专栏 202

如何理解Python关键字yield

语句中的对象都可以叫做可迭代对象,例如:lists,strings,files等等。这些可迭代对象使用非常方便因为它能如你所愿的尽可能读取其中的元素,但是你不得不把所有的值存储在内存中,当它有大量元素的时候这并不一定总是你想要的。可迭代对象是任何你可以从其得到一个迭代器对象的任意对象(译注:调用内建函数iter()),迭代器是能让你在可迭代对象上进行迭代的对象(译注:这里好绕,迭代器实现了。方法的类都是可迭代对象,此外,可迭代对象还可以用在zip,map等函数中,当一个可迭代对象作为参数传递给内建函数。

mohen_777的博客 2093

Python 生成器

生成器算得上是Python语言中最吸引人的特性之一,生成器其实是一种特殊的迭代器,不过这种迭代器更加优雅。它不需要手动编写__iter__()和__next__()方法,只需要一个yiled关键字。• 生成器一定是迭代器(反之不成立)。• 因此任何生成器也是以一种懒加载的模式生成值。

qq_60271706的博客 1170

6.迭代器与生成器

一.可迭代对象 在介绍迭代器与生成器之前,我们首先来介绍一下可迭代对象。可迭代对象,顾名思义,就是可以“迭代”的对象。所谓迭代,就是一个 通过for循环遍历出对象中所有元素 的过程。那么可迭代对象,便是可以实现这个过程的对象了。 在Python中,很多对象都是可迭代的,比如列表(list)、字典(dict)、元组(tuple)、集合(set)、字符串(str)、字节(bytes)等等,并且这些...

MashalC的博客 229

迭代器和生成器

可迭代对象:任何你可以用for ... in ...的对象都是可迭代对象, list,strings, files, sockets…。 当你创建一个list的时候,你一个个的获取list里面的值,这就是可迭代对象 mylist = [1, 2, 3] for i in mylist: print(i) 迭代器:它是一个带状态的对象,他能在你调用next(...

weixin_30951389的博客 81

Python的可迭代对象、迭代器以及生成器

定义 可迭代对象:实现了 iter() 方法的对象; 迭代器:实现了_iter_() 和 next() 方法的对象; 生成器:具有 yield 关键字的对象。 &;amp 迭代器是一种特殊的可迭代对象; 生成器是一种特殊的迭代器,自动实现了迭代协议,即_iter_()和next()方法,无需自己定义。 ...

m_pNext的博客 312

python 可迭代对象_Python3学习笔记(十四):可迭代对象、迭代器和生成器

记得在刚开始学Python的时候,看到可迭代对象(iterable)、迭代器(iterator)和生成器(generator)这三个名词时,完全懵逼了,根本就不知道是啥意识。现在以自己的理解来详解下这三者的关系。一、可迭代对象(iterable)我们知道,在Python世界里,一切皆对象。对象根据定义的维度,又可以分为各种不同的类型,比如:文件对象,字符串对象,列表对象。。。等等。那什么对象才能叫...

weixin_39889544的博客 127

httpentity 获取返回对象_一文读懂Python可迭代对象、迭代器和生成器

来源:Python中文社区ID:python-china我们都知道,序列可以迭代。但是,你知道为什么吗? 本文来探讨一下迭代背后的原理。序列可以迭代的原因:iter 函数。解释器需要迭代对象 x 时,会自动调用 iter(x)。内置的 iter 函数有以下作用:(1) 检查对象是否实现了 iter 方法,如果实现了就调用它,获取一个迭代器。 (2) 如果没有实现 iter 方法,但是实现了 get...

weixin_39627699的博客 251

python中的迭代器、可迭代对象、生成器和yield

print("__iter__被执行了...")print("__next__被执行了...")# raise StopIteration用于提前终止一个迭代器中的循环print(isinstance(obj_list,Iterable)) # 输出:Trueprint(isinstance(obj_list,Iterator)) # 输出:Trueprint(i)输出:TrueTrue__iter__被执行了...__next__被执行了...2__next__被执行了...

m0_46610658的博客 970
上一篇: (二)K8s学习笔记——使用Kubeadm搭建K8s集群(单master节点)
MegaBytes
博客等级 码龄8年 45粉丝 115原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值