11 迭代器|生成器|协程

文章目录

  • 迭代器
    • 可迭代对象
    • 可迭代对象的本质
    • iter()函数与 next()函数
    • 迭代器 Iterator
      • 样例
    • for...in...循环的本质
    • 使用的场景--斐波那契数列
    • list和tuple也可以接收可迭代对象
  • 生成器
    • 简介
    • 创建生成器
      • 方法一
      • 方法二
      • 总结
    • 使用 send 唤醒
  • 协程
    • 协程和线程差异
    • 简单实现协程
    • greenlet
    • gevent
      • 安装
      • gevent 的使用方法
      • 给程序打补丁
      • gevent的常用方法
    • python最新接口
    • 最后的示例
      • 并行下载

迭代器

迭代是访问集合元素的一种方式。迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。

可迭代对象

我们已经知道可以对listtuplestr 等类型的数据使用 for…in…的循环语法从其中依次拿到数据进行使用,我们把这样的过程称为遍历,也叫迭代。

我们把可以通过 for…in…这类语句迭代读取一条数据供我们使用的对象称之为可迭代对象(Iterable)。

可以使用 isinstance() 判断该对象是否是一个可迭代的对象

In [50]: from collections.abc import Iterable
In [51]: isinstance([], Iterable)
Out[51]: True
In [52]: isinstance({}, Iterable)
Out[52]: True
In [53]: isinstance('abc', Iterable)
Out[53]: True
In [54]: isinstance(mylist, Iterable)
Out[54]: False
In [55]: isinstance(100, Iterable)
Out[55]: False

可迭代对象的本质

可迭代对象通过__iter__方法向我们提供一个迭代器,我们在迭代一个可迭代对
象的时候,实际上就是先获取该对象提供的一个迭代器,然后通过这个迭代器来依次获取对象中的每一个数据. 那么也就是说,一个具备了__iter__方法的对象,就是一个可迭代对象,所以关键就在于我们要如何去重写这一个__iter__方法。

iter()函数与 next()函数

list、tuple 等都是可迭代对象,我们可以通过 iter()函数获取这些可迭代对象的迭代器。然后我们可以对获取到的迭代器不断使用 next()函数来获取下一条数据。

iter()函数实际上就是调用了可迭代对象的__iter__方法

注意,当我们已经迭代完最后一个数据之后,再次调用 next()函数会抛出StopIteration 的异常,来告诉我们所有数据都已迭代完成,不用再执行 next()函数了

迭代器 Iterator

通过上面的分析,我们已经知道,迭代器是用来帮助我们记录每次迭代访问到的位置,当我们对迭代器使用 next()函数的时候,迭代器会向我们返回它所记录位置的下一个位置的数据。实际上,在使用 next()函数的时候,调用的就是迭代器对象的__next__方法(Python3 中是对象的__next__方法)。所以,我们要想构造一个迭代器,就要实现它的__next__方法。但这还不够,python 要求迭代器本身也是可迭代的,所以我们还要为迭代器实现__iter__方法,而__iter__方法要返回一个迭代器,迭代器自身正是一个迭代器,所以迭代器的__iter__方法返回自身即可

一个实现了__iter__方法和__next__方法的对象,就是迭代器。

样例

from collections.abc import Iterable
# 自定义可迭代对象
class MyList:def __init__(self):self.mylist = []def add(self,num):self.mylist.append(num)# 此处是关键,定义了这一个之后,就相当于是让这个对象是可迭代对象def __iter__(self):return MyIterator(self)# 自定义迭代器
class MyIterator:def __init__(self,mylist):self.mylist:MyList = mylistself.current = 0def __iter__(self):return selfdef __next__(self):if self.current > len(self.mylist.mylist) - 1:# 此处一定要实现抛出StopIteration异常raise StopIterationelse:current = self.currentself.current = self.current + 1return self.mylist.mylist[current]if __name__ == '__main__':mylist = MyList()mylist.add(1)mylist.add(2)mylist.add(3)for i in mylist:print(i)print(isinstance(mylist,Iterable))

for…in…循环的本质

for item in Iterable 循环的本质就是先通过 iter()函数获取可迭代对象 Iterable 的迭代器,然后对获取到的迭代器不断调用 next()方法来获取下一个值并将其赋值给item,当遇到 StopIteration 的异常后循环

使用的场景–斐波那契数列


class FibIterator(object):"""斐波那契数列迭代器"""def __init__(self, n):""":param n: int, 指明生成数列的前 n 个数"""self.n = n# current 用来保存当前生成到数列中的第几个数了self.current = 0# num1 用来保存前前一个数,初始值为数列中的第一个数 0self.num1 = 0# num2 用来保存前一个数,初始值为数列中的第二个数 1self.num2 = 1def __next__(self):"""被 next()函数调用来获取下一个数"""if self.current < self.n:num = self.num1self.num1, self.num2 = self.num2, self.num1+self.num2self.current += 1return numelse:raise StopIterationdef __iter__(self):"""迭代器的__iter__返回自身即可"""return selfif __name__ == '__main__':fib = FibIterator(10)for num in fib:print(num, end=" ")

list和tuple也可以接收可迭代对象

除了 for 循环能接收可迭代对象,list、tuple 等也能接收

li = list(FibIterator(15))
print(li)
tp = tuple(FibIterator(6))
print(tp)

生成器

简介

利用迭代器,我们可以在每次迭代获取数据(通过 next()方法)时按照特定的规律进行生成。但是我们在实现一个迭代器时,关于当前迭代到的状态需要我们自己记录,进而才能根据当前状态生成下一个数据。为了达到记录当前状态,并配合next()函数进行迭代使用,我们可以采用更简便的语法,即生成器(generator)。生成器是一类特殊的迭代器。

创建生成器

方法一

实际上就是把一个列表生成式的 [ ] 改成 ( )

my_generator = ( x*2 for x in range(5))
print(my_generator)
print(next(my_generator))
print(next(my_generator))
print(next(my_generator))
print(next(my_generator))
print(next(my_generator))

对于生成器来说,我们可以按照迭代器的使用方法来使用,即可以通过 next()函数、for 循环、list()等方法进行迭代

方法二

generator 非常强大。如果推算的算法比较复杂,用类似列表生成式的 for 循环无法实现的时候,还可以用函数来实现。

在使用生成器实现的方式中,我们将原本在迭代器__next__方法中实现的基本逻辑放到一个函数中来实现,但是将每次迭代返回数值的 return 换成了 yield,此时新定义的函数便不再是函数,而是一个生成器了。简单来说:只要在 def 中有yield 关键字的 就称为 生成器此时按照调用函数的方式( 案例中为 F = fib(5) )使用生成器就不再是执行函数体了,而是会返回一个生成器对象( 案例中为 F ),然后就可以按照使用迭代器的方式来使用生成器了。

# 含有yield的函数称为生成器
def fib(n):current = 0num1, num2 = 0, 1while current < n:num = num1num1, num2 = num2, num1 + num2current += 1# print(num,end=' ')yield numreturn 'done'# F是一个生成器,支持next
F=fib(10)print(F)# for i in F:
#     print(i,end=' ')# 迭代生成
l=[ i for i in F ]
print(l)# 想要拿取return当中的值
try:next(F)
except StopIteration as e:print("\n返回值为:{}".format(e.value))

用 for 循环调用 generator 时,发现拿不到 generator 的 return 语句的返回
值。如果想要拿到返回值,必须捕获 StopIteration 错误,返回值包含在
StopIteration 的 value 中

这里还有一个小细节,读者可能回想为什么我们在使用已经做好的list时候对于list进行for in处理,可以多次处理多次拿值,但是对于我们上文写的迭代器为什么不行?原因就在于我们实际上list叫做可迭代对象,它实际上所采用的是我们最上面的那种手法,定义两个类的手法,每次迭代,都会有一个新的迭代器来运行。

总结

  1. 使用了 yield 关键字的函数不再是函数,而是生成器。(使用了 yield 的函数
    就是生成器)
  2. yield 关键字有两点作用:
    – 保存当前运行状态(断点),然后暂停执行,即将生成器(函数)挂起
    – 将 yield 关键字后面表达式的值作为返回值返回,此时可以理解为起到了 return 的作用
  3. 可以使用 next()函数让生成器从断点处继续执行,即唤醒生成器(函数)
  4. Python3 中的生成器可以使用 return 返回最终运行的返回值

使用 send 唤醒

我们除了可以使用 next()函数来唤醒生成器继续执行外,还可以使用 send()函数来唤醒执行。使用 send()函数的一个好处是可以在唤醒的同时向断点处传入一个附加数据

def gen():i=0while i<5:temp=yield iprint(temp)i+=1g=gen()next(g)
g.send('hello')

协程

协程,又称微线程,纤程。英文名 Coroutine。

协程是 python 个中另外一种实现多任务的方式,只不过比线程更小占用更小执行单元(理解为需要的资源)。 为啥说它是一个执行单元,因为它自带 CPU 上下文。这样只要在合适的时机, 我们可以把一个协程 切换到另一个协程。 只要这个过程中保存或恢复 CPU 上下文那么程序还是可以运行的。

通俗的理解:在一个线程中的某个函数,可以在任何地方保存当前函数的一些临时变量等信息,然后切换到另外一个函数中执行,注意不是通过调用函数的方式做到的,并且切换的次数以及什么时候再切换到原来的函数都由开发者自己确定。

协程和线程差异

在实现多任务时, 线程切换从系统层面远不止保存和恢复 CPU 上下文这么简单。操作系统为了程序运行的高效性每个线程都有自己缓存 Cache 等等数据,操作系统还会帮你做这些数据的恢复操作。 所以线程的切换非常耗性能。但是协程的切换只是单纯的操作 CPU 的上下文,所以一秒钟切换个上百万次系统都抗的住。

简单实现协程

import timedef work1():while True:print("----work1---")yieldtime.sleep(0.5)def work2():while True:print("----work2---")yieldtime.sleep(0.5)def main():w1 = work1()w2 = work2()while True:next(w1)next(w2)if __name__ == "__main__":main()

greenlet

基本的使用方法:

from greenlet import greenlet
import timedef test1():while True:print("---A--")gr2.switch()time.sleep(0.5)def test2():while True:print("---B--")gr1.switch()time.sleep(0.5)gr1 = greenlet(test1)
gr2 = greenlet(test2)
#切换到 gr1 中运行
gr1.switch()

gevent

greenlet 已经实现了协程,但是这个还的人工切换,是不是觉得太麻烦了,python 还有一个比 greenlet 更强大的并且能够自动切换任务的模块 gevent,其原理是当一个 greenlet 遇到 IO(指的是 input output 输入输出,比如网络、文件操作等)操作时,比如访问网络,就自动切换到其他的 greenlet,等到 IO 操作完成,再在适当的时候切换回来继续执行。

由于 IO 操作非常耗时,经常使程序处于等待状态,有了 gevent 为我们自动切换协程,就保证总有协程在运行,而不是等待 IO时间

参考链接

安装

pip install gevent -i https://pypi.tuna.tsinghua.edu.cn/simple

gevent 的使用方法

gevent.spawn 接口使用方法gevent.spawn(函数名,传参)
import geventdef f(n):for i in range(n):print(gevent.getcurrent(), i)#用来模拟一个耗时操作,注意不是 time 模块中的 sleepgevent.sleep(1)g1 = gevent.spawn(f, 5)
g2 = gevent.spawn(f, 5)
g3 = gevent.spawn(f, 5)
g1.join()
g2.join()
g3.join()

运行结果:

<Greenlet at 0x7fa70ffa1c30: f(5)> 0
<Greenlet at 0x7fa70ffa1870: f(5)> 0
<Greenlet at 0x7fa70ffa1eb0: f(5)> 0
<Greenlet at 0x7fa70ffa1c30: f(5)> 1
<Greenlet at 0x7fa70ffa1870: f(5)> 1
<Greenlet at 0x7fa70ffa1eb0: f(5)> 1
<Greenlet at 0x7fa70ffa1c30: f(5)> 2
<Greenlet at 0x7fa70ffa1870: f(5)> 2
<Greenlet at 0x7fa70ffa1eb0: f(5)> 2
<Greenlet at 0x7fa70ffa1c30: f(5)> 3
<Greenlet at 0x7fa70ffa1870: f(5)> 3
<Greenlet at 0x7fa70ffa1eb0: f(5)> 3
<Greenlet at 0x7fa70ffa1c30: f(5)> 4
<Greenlet at 0x7fa70ffa1870: f(5)> 4
<Greenlet at 0x7fa70ffa1eb0: f(5)> 4

给程序打补丁

猴子补丁作用:
monkey patch 指的是在执行时动态替换,通常是在 startup 的时候. 用过 gevent 就会知道,会在最开头的地方 gevent.monkey.patch_all();把标准库中的 thread/socket 等给替换掉.这样我们在后面使用 socket的时候能够跟寻常一样使用,无需改动不论什么代码,可是它变成非堵塞的了

from gevent import monkey
import gevent
import random
import time# 这句话是关键
monkey.patch_all()def coroutine_work(coroutine_name):for i in range(10):print(coroutine_name, i)time.sleep(random.random())gevent.joinall([gevent.spawn(coroutine_work, "work1"),gevent.spawn(coroutine_work, "work2")
])

gevent的常用方法

常用方法说明
gevent.spawn()创建一个普通的 Greenlet 对象并切换
gevent.spawn_later(seconds=3)延时创建一个普通的 Greenlet 对象并切换
gevent.spawn_raw()创建的协程对象属于一个组
gevent.getcurrent()返回当前正在执行的 greenlet
gevent.joinall(jobs)将协程任务添加到事件循环,接收一个任务列表
gevent.wait()可以替代 join 函数等待循环结束,也可以传入协程对象列表
gevent.kill()杀死一个协程
gevent.killall()杀死一个协程列表里的所有协程
monkey.patch_all()非常重要,会自动将 python 的一些标准模块替换成 gevent框架

python最新接口

链接

官方文档:
链接
链接

最后的示例

并行下载

from gevent import monkey
import gevent
import urllib.request# 有耗时操作时需要
monkey.patch_all()def my_downLoad(url):print('GET: %s' % url)resp = urllib.request.urlopen(url)data = resp.read()print('%d bytes received from %s.' % (len(data), url))gevent.joinall([gevent.spawn(my_downLoad, 'http://www.baidu.com/'),gevent.spawn(my_downLoad, 'http://www.cskaoyan.com/'),gevent.spawn(my_downLoad, 'http://www.qq.com/'),
])

以及

from gevent import monkey
import gevent
import urllib.request#有 IO 才做时需要这一句
monkey.patch_all()def my_downLoad(file_name, url):print('GET: %s' % url)resp = urllib.request.urlopen(url)data = resp.read()with open(file_name, "wb") as f:f.write(data)print('%d bytes received from %s.' % (len(data), url))gevent.joinall([gevent.spawn(my_downLoad,"7a082c0dde36eac2205a088397aaf295.jpg",'http://qzs.qq.com/qzone/v6/v6_config/upload/7a082c0dde36eac2205a088397aaf295.jpg'),gevent.spawn(my_downLoad,"da8e974dc_is.jpg",'https://pic1.zhimg.com/da8e974dc_is.jpg'),])

上面的 url 可以换为自己需要下载视频、音乐、图片等url

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/36950.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微PE工具箱实现U盘重装Windows系统

教程来源 U盘重装Windows系统&#xff08;微PE工具箱&#xff09;_哔哩哔哩_bilibili 加上自己的一丢丢理解&#xff0c;如果你觉得长视频看了犯困&#xff0c;不如看看我的理解文章说不定能够帮助到你 准备工作 到这个网站使用迅雷下载免费无插件的官方镜像MSDN, 我告诉你…

JVM笔记 —— 出现内存溢出错误时时如何排查

一、出现内存溢出的几种情况 内存溢出错误分为StackOverflowError和OutOfMemoryError&#xff0c;前者是栈中出现溢出&#xff0c;后者一般是堆或方法区出现溢出&#xff0c;简称OOM 1. 栈溢出 StackOverflowError 栈溢出一般都是因为没有正确的结束递归导致的&#xff0c;无…

Linux中安装MySQL8版本,安装MySQL步骤,MySQL8离线安装

Linux中安装MySQL8版本的步骤如下&#xff1a; 1.检查下libaio.so.1的位置 [roottdx ]# whereis libaio.so.1 libaio.so: /usr/lib64/libaio.so.1 如果没有找到该文件 (1).在线安装 [roottdx ]# yum install -y libaio (2).离线安装&#xff1a; 上传之后执行命令安装&#…

pymysql 库 - python 操作 mysql

环境&#xff1a; Win10 x64 Python 3.7 PyMySQL 1.0.2 MySQL 8.0.27 1 安装 pip install pymysql 2 地址 https://pypi.org/project/pymysql/ 3.1 数据库版本查询 (search_version.py) import pymysql# 打开数据库连接 try:db pymysql.connect(hostlocalhost, userr…

python安装第三方包时报错:...\lib\site-packages\pip\_vendor\urllib3\response.py...

安装redis第三方包&#xff1a; pip install redis报错现象&#xff1a; 解决方法&#xff1a;使用以下命令可成功安装 pip install redis -i http://pypi.douban.com/simple --trusted-host pypi.douban.com

关于网络入侵检测领域使用Spark/Flink等计算框架做分布式

关于网络入侵检测领域使用Spark/Flink等计算框架做分布式 0、引言1 基于LightGBM的网络入侵检测研究2 基于互信息法的智能化运维系统入侵检测Spark实现3 基于Spark的车联网分布式组合深度学习入侵检测方法4 基于Flink的分布式在线集成学习框架研究5 基于Flink的分布式并行逻辑回…

mongodb基础

mongodb语法 参考文档&#xff1a;https://docs.mongodb.com/manual/reference/ BSON Types BSON Type有2种标识符&#xff0c;整形和字符串 类型数值字符串说明Double1“double”String2“string”Object3“object”Array4“array”Binary data5“binData”Undefined6“un…

8.9黄金最新行情走势分析及短线交易策略

近期有哪些消息面影响黄金走势&#xff1f;黄金多空该如何研判&#xff1f; ​黄金消息面解析&#xff1a;周三&#xff08;8月9日&#xff09;现货黄金维持震荡&#xff0c;目前交投于1930美元附近&#xff0c;隔日现货黄金盘中震荡下行&#xff0c;失守1930关口并在美盘时段…

【Spring】-Spring的IoC和DI

作者&#xff1a;学Java的冬瓜 博客主页&#xff1a;☀冬瓜的主页&#x1f319; 专栏&#xff1a;【Framework】 主要内容&#xff1a;什么是spring&#xff1f;IoC容器是什么&#xff1f;如何使代码解耦合&#xff1f;IoC的核心原理&#xff0c;IoC的优点。依赖注入/对象装配/…

【ARM 嵌入式 编译系列 10 -- GCC 编译缩减可执行文件 elf 文件大小】

文章目录 GCC 如何缩减可执行文件size测试代码 上篇文章&#xff1a;ARM 嵌入式 编译系列 9-- GCC 编译符号表&#xff08;Symbol Table&#xff09;的详细介绍 下篇文章&#xff1a;ARM 嵌入式 编译系列 10.1 – GCC 编译缩减可执行文件 elf 文件大小 GCC 如何缩减可执行文件s…

Linux下在qtcreator中创建qt程序

目录 1、新建项目 2、单工程项目创建 3、多工程项目创建 4、添加子工程&#xff08;基于多工程目录结构&#xff09; 5、 .pro文件 1、新建项目 切换到“编辑”界面&#xff0c;点击菜单栏中的“文件”-“新建文件或项目” 2、单工程项目创建 只有一个工程的项目&#…

Axure RP移动端高保真CRM办公客户管理系统原型模板及元件库

Axure RP移动端高保真CRM办公客户管理系统原型模板及元件库&#xff0c;一套典型的移动端办公工具型APP Axure RP原型模板&#xff0c;可根据实际的产品需求进行扩展&#xff0c;也可以作为移动端原型设计的参考案例。为提升本作品参考价值&#xff0c;在模板设计过程中尽量追求…

chatGPT应用于房地产行业

作为 2023 年的房地产专业人士&#xff0c;您无疑认识到技术对行业的重大影响。近年来&#xff0c;一项技术进步席卷了世界——人工智能。人工智能彻底改变了房地产业务的各个方面&#xff0c;从简化管理任务到增强客户互动。 在本文中&#xff0c;我们将探讨几种巧妙的人工智…

HTML 语言简介

1.概述 HTML 是网页使用的语言&#xff0c;定义了网页的结构和内容。浏览器访问网站&#xff0c;其实就是从服务器下载 HTML 代码&#xff0c;然后渲染出网页。 HTML 的全名是“超文本标记语言”&#xff08;HyperText Markup Language&#xff09;&#xff0c;上个世纪90年代…

zabbix自动注册服务器以及部署代理服务器

文章目录 Zabbix自动注册服务器及部署代理服务器一.zabbix自动注册1.什么是自动注册2.环境准备3.zabbix客户端配置4.在 Web 页面配置自动注册5.验证自动注册 二.部署 zabbix 代理服务器1.分布式监控的作用&#xff1a;2.环境部署3.代理服务器配置4.客户端配置5.web页面配置5.1 …

MS9940T 国产 模拟前端AFE芯片 11-15 节锂电池或磷酸盐电池管理芯片 替代BQ76940

产品简述 MS9940T 是模拟前端 (AFE) 芯片&#xff0c;支持11 到 15 组电池串联。通过 I 2 C &#xff0c;主机控制器 可以使用 MS9940T 来实现电池组管理功能&#xff0c;例如监控&#xff08;电池电压、电池组电流、电池组 温度&#xff09;、保护&#xff08;控制…

分类预测 | MATLAB实现GWO-BiLSTM-Attention多输入分类预测

分类预测 | MATLAB实现GWO-BiLSTM-Attention多输入分类预测 目录 分类预测 | MATLAB实现GWO-BiLSTM-Attention多输入分类预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.GWO-BiLSTM-Attention 数据分类预测程序 2.代码说明&#xff1a;基于灰狼优化算法&#xff08…

vuejs 设计与实现 - 组件的实现原理

1.渲染组件 如果是组件则&#xff1a;vnode .type的值是一个对象。如下&#xff1a; const vnode {type: MyComponent,}为了让渲染器能处理组件类型的虚拟节点&#xff0c;我们还需要在patch函数中对组件类型的虚拟节点进行处理&#xff0c;如下&#xff1a; function patc…

CentOS7.9 禁用22端口,使用其他端口替代

文章目录 业务场景操作步骤修改sshd配置文件修改SELinux开放给ssh使用的端口修改防火墙&#xff0c;开放新端口重启sshd生效 相关知识点介绍sshd服务SELinux服务firewall.service服务 业务场景 我们在某市实施交通信控平台项目&#xff0c;我们申请了一台服务器&#xff0c;用…

学习Vue:列表渲染(v-for)

在 Vue.js 中&#xff0c;实现动态列表的显示是非常常见的需求。为了达到这个目的&#xff0c;Vue 提供了 v-for 指令&#xff0c;它允许您迭代一个数组或对象&#xff0c;将其元素渲染为列表。然而&#xff0c;在使用 v-for 时&#xff0c;key 属性的设置也非常重要&#xff0…