转自http://blog.itpub.net/29018063/viewspace-2079767

大家在学习python开发时可能经常对迭代器、生成器、yield关键字用法有所疑惑,在这篇文章将从理论+程序调试验证的方式详细讲解这部分知识,话不多说,直接进入主题。

一、迭代器(Iterater):

首先介绍迭代器,迭代器是访问集合元素的一种方式,迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。是不是觉得跟for循环很像?但是迭代器有几个特性需记住:

1、访问者不需要关心迭代器内部结构,只需不断执行next()方法获取下一个内容;

2、不能随机访问集合中的某个值,只能从头到尾顺序的读取;

3、访问到一半时不能回退,不能访问之前的值;

4、适合遍历很大的数据集合,节省内存,提升速度。

根据以上几个特性应该对迭代器有所了解了吧,我这里再举一个例子便于加深理解,大家都玩过linux,在linux中有两个命令,vi和cat,作用都是显示出文件的内容,而当一个文件很大(比如1G)时,使用vi命令打开文件则明显变慢,都有体会吧,会卡很久;而使用cat命令则没有这个问题,无论多大的文件,执行cat命令时都会马上从文件第一行数据开始打印;其实原因也很简单,使用vi时需要将整个文件加载到内存中再显示出来,而使用cat时则从文件第一行记录开始逐行的读取到内存中显示,而已读取过的内容则直接释放掉,这样每次读取到内存中只有一行记录响应速度当然快了。

其实这里的cat正是运用了迭代器的思想,迭代器每次顺序取集合中的一个值到内存,用完即作废,再取下一个值,对应特性1,对于很大的文件遍历则速度很快,对应特性4;则缺点也是明显的,迭代器不能取集合中间某个值,对应特性2;前一个值读取完即回收内存,所以无法重复读取,对应特性3;讲到这里大家应该已经能充分理解迭代器的原理了,下面进行代码演示:

1、创建一个迭代器:

a = iter(["wang","xuqian","xiaozhuzi"])    # 已创建一个迭代器对象,设置好需要迭代的值

2、遍历迭代器数据:

print(a.__next__())

print(a.__next__())

print(a.__next__())    # 有三个值,于是执行三次next()方法

3、结果:

wang

xuqian

xiaozhuzi

就是这么简单,由于迭代器的特性,我们只能顺序依次进行取值,不能像list那样可以取集合中的任意值,在这里三个值都取出后如果再执行a.__next__(),则会报错:“已停止迭代”

Traceback (most recent call last):

print(a.__next__())

StopIteration

二、生成器(Generator)和yield关键字:    生成器定义:当一个函数被调用时,返回一个迭代器,那么这个函数就叫做生成器,如果函数中包括yield语法,则这个函数就是一个生成器

yield:效果就是使函数中断,并保存中断的状态,中断后,代码可以继续往下执行,过一段时间还可以重新调用这个函数,并且可以从上次yield的下面的一句代码开始执行;yield可以返回值,也可以接收send来的参数。

生成器和yield的解释比较抽象难以理解,别急,下面通过调试代码的方式展示他们的用法,以及yield的魅力!~

def cash(account):                # 先定义一个函数,接收一个参数

while account > 0:            # 当参数>0时

account -= 100                 #减去100

yield 100                     #中断函数吗,返回100

print("又来取钱啦!")        #打印

根据生成器的定义,此时cash函数即为一个生成器,作为一个生成器,当被调用后产生的生成器对象肯定是支持迭代器接口,也就是生成器返回一个迭代器,下面我们进行验证:

a = cash(500)        # 调用生成器,生成迭代器

print(a.__next__())    # 运用迭代器的next()方法打印第一个迭代值

执行结果为:100

以上验证了调用生成器cash后的确生成了一个迭代器,关于迭代器应该没有什么疑问了,可以持续next()取值直到 account == 0;而大家现在一定有个疑问,这里面的yield关键字的作用是什么?为什么打印结果没有print("又来取钱啦!")?那么下面我跑一遍程序,通过结果来分析代码执行顺序:

def cash(account):

while account > 0:

account -= 100

yield 100

print("从我开始往下执行!") print("代码已经执行完啦")

a = cash(500)

print("第0次执行我")

print(a.__next__())

print("第一次执行我")

print(a.__next__())

运行结果为:

第0次执行我

100

第一次执行我

从我开始往下执行!

代码已经执行完啦!

100

从运行结果可以推断出,当程序进行到a = cash(500) 时,函数并没有进行调用而是继续往下走打印“第0次执行我”,为什么?其实正是因为在函数定义的时候,检测到函数中写了yield关键字,此时这个函数就变成了一个生成器,于是函数暂停运行;当执行到第一个print(a.__next__()) 时,才开始真正的调用函数了,函数执行到yield 100 时,根据yield的特性,函数中断返回100并打印,程序继续向下执行打印“第一次执行我”;当执行到第二个print(a.__next__()) 时,继续调用函数,根据yield的特性,会接着上一次中断的位置继续执行函数,于是打印“从我开始往下执行!”“代码已经执行完啦”,而根据函数中的while循环,会再次执行循环代码,直到 yield 100,函数再次中断返回100并打印。到此,程序执行完毕!

三、yield单线程异步并发实现

根据以上程序演示,大家应该对yield和生成器的特性理解更加深刻了吧,yield打破了常规的代码执行顺序,甚至能跳出循环,而且下一次调用函数(生成器)时还可以接着上次的代码向下执行,是不是很牛X?那么,yield的特性到底有什么用呢?

举个例子,我去银行取钱,加入取款额度较大,100万~,银行则需要审核、调配资金等手续,假如需要1天的时间,那么这一天我都要等在那里,这显然是不合理的吧。最好的办法是我继续做别的事情,等银行审批结束后再通知我去取钱。对于我们写的代码也是一样,由于我们是单线程串行执行代码,当调用一个函数时,如果这个函数迟迟不返回结果怎么办?按照之前的思路,那么程序只能卡死在那里一直等待,就和去取钱等银行审批一个道理;而yield的特性,打破了这一约束,使得我们在单线程编程的过程中,依然可以实现异步并发!下面再看一段代码:

import time

def consumer(name):

print("%s 准备吃烧烤啦!" %name)

while True:

shaokao = yield

print("烧烤%s被%s吃了!" %(shaokao,name))

def producter(name):

c = consumer("A")

c1 = consumer("B")

c.__next__()

c1.__next__()

print("开始生火烤串啦!")

for i in range(3):

time.sleep(1)

print("烤了两串羊肉")

c.send(i)

c1.send(i)

producter("jiaqi")

根据前面讲的知识,应该可以得出执行结果:

A 准备吃烧烤啦!

B 准备吃烧烤啦!

开始生火烤串啦!

烤了两串羊肉

烧烤0被A吃了!

烧烤0被B吃了!

烤了两串羊肉

烧烤1被A吃了!

烧烤1被B吃了!

烤了两串羊肉

烧烤2被A吃了!

烧烤2被B吃了!

这里就实现了异步并发控制,一个函数和生成器之间调用,通过yield实现;这里面还有个知识点,c.send(i),前面讲过yield不仅能返回值,而且还能接收值,在这里send()大家可以理解为与next()一样,都是触发调用生成器中的代码,但next()可以理解为传一个空值给yield,send()则可传一个实际的值给yield。以上代码中将i值传给了生成器consumer中的Yield

python生成器单线程_【Python】迭代器、生成器、yield单线程异步并发实现详解相关推荐

  1. python 归一化还原_对python3 一组数值的归一化处理方法详解

    1.什么是归一化: 归一化就是把一组数(大于1)化为以1为最大值,0为最小值,其余数据按百分比计算的方法.如:1,2,3.,那归一化后就是:0,0.5,1 2.归一化步骤: 如:2,4,6 (1)找出 ...

  2. python 数列筛选_对numpy中的数组条件筛选功能详解

    在程序设计中,时常会遇到数据的唯一化.相同.相异信息的提取等工作,在格式化的向量存储矩阵中南,numpy能够提供比较不错的快速处理功能. 1,唯一化的实现: In [63]: data = np.ar ...

  3. python pptx教学_基于python-pptx库中文文档及使用详解

    个人使用样例及部分翻译自官方文档,并详细介绍chart的使用 一:基础应用 1.创建pptx文档类并插入一页幻灯片 from pptx import Presentation prs = Presen ...

  4. Python异步并发机制详解,让你的代码运行效率就像搭上了火箭!!!

    文章目录 探究低层建筑:asyncio 同步/异步 了解一下协程 相对于线程,协程的优势 同步代码转异步代码 通过asyncio讲解协程 所以,代码到底怎么写?!!! 协程可以做哪些事? 定义协程函数 ...

  5. python调用shell脚本的参数_使用python执行shell脚本 并动态传参 及subprocess的使用详解

    最近工作需求中 有遇到这个情况 在web端获取配置文件内容 及 往shell 脚本中动态传入参数 执行shell脚本这个有多种方法 最后还是选择了subprocess这个python标准库 subpr ...

  6. python整数池_对Python中小整数对象池和大整数对象池的使用详解

    1. 小整数对象池 整数在程序中的使用非常广泛,Python为了优化速度,使用了小整数对象池, 避免为整数频繁申请和销毁内存空间. Python 对小整数的定义是 [-5, 256] 这些整数对象是提 ...

  7. python计算各类型电影的评分_【Python数据科学实战项目】之 基于MovieLens的影评趋势分析|详解...

    原标题:[Python数据科学实战项目]之 基于MovieLens的影评趋势分析|详解 注:图片源于https://movielens.org/ 1. 项目任务 1.1 数据来源 本项目使用Group ...

  8. [python opencv 计算机视觉零基础到实战] 四、了解色彩空间及其详解

    一.学习目标 了解什么是色彩空间 了解opencv中色彩空间的转换 目录 [python opencv 计算机视觉零基础到实战] 一.opencv的helloworld [[python opencv ...

  9. Python的Django框架中forms表单类的使用方法详解2

    用户表单是Web端的一项基本功能,大而全的Django框架中自然带有现成的基础form对象,本文就Python的Django框架中forms表单类的使用方法详解. Form表单的功能 自动生成HTML ...

最新文章

  1. 悬而未决的AI竞赛:全球企业人工智能发展现状
  2. Hopfield神经网络和TSP问题
  3. 机器学习特征构建_使用Streamlit构建您的基础机器学习Web应用
  4. python有多少库存_库存究竟多少才算合理?
  5. 【Python 标准库学习】系统相关的参数和函数库 — sys
  6. gblfy_IDEA常用快捷键技巧
  7. 【离散数学】命题逻辑联结词的自然语言表述
  8. 闩锁电流_IGBT——闩锁(Lanchup)效应
  9. Oracle密码过期及账户解锁的问题
  10. 【MDVRP】基于matlab水滴算法求解多仓库车辆路径规划问题【含Matlab源码 1310期】
  11. devc编译器中的socket编程爬坑日记
  12. HTML页面的全屏显示-Fullscreen API
  13. 智慧路灯解决方案-最新全套文件
  14. 数据结构与算法分析题库
  15. 计算机控制教师端,摆脱学校机房教师端控制的方法汇总
  16. 上传文件到本地操作和上传到Azure云上
  17. 根据经纬度坐标点返回所在行政区域实现
  18. 怎么删除计算机管理员用户密码,解除计算机管理员密码的方法
  19. 获取属性配置config文件
  20. mac系统升级后git使用不了

热门文章

  1. 比CRUD多一点儿(三):UPDATE、DELETE语句
  2. 如何提取cocos iOS应用程序APP与游戏安装包里的资源与文件
  3. nodejs系列(二)REPL交互解释 事件循环
  4. IOS之UITabBarController
  5. Spring异常解决 java.lang.NullPointerException,配置spring管理hibernate时出错
  6. sql exists用法_新同事不讲武德,这SQL语句写得忒野了
  7. cas无法使用_并发编程中cas的这三大问题你知道吗?
  8. python子进程通信_python执行子进程实现进程间通信的方法
  9. linux内核采取,采用动态加载模块的方式Linux内核编译
  10. 怎么去除图像亮度对图像质量评价的影响_图像质量评估指标 SSIM / PSNR / MSE