转自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】迭代器、生成器、yield单线程异步并发实现详解的更多相关文章

  1. Python迭代器生成器与生成式

    Python迭代器生成器与生成式 什么是迭代 迭代是重复反馈过程的活动,其目的通常是为了逼近所需目标或结果.每一次对过程的重复称为一次"迭代",而每一次迭代得到的结果会作为下一次迭 ...

  2. php为什么需要异步编程?php异步编程的详解(附示例)

    本篇文章给大家带来的内容是关于php为什么需要异步编程?php异步编程的详解(附示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 我对 php 异步的知识还比较混乱,写这篇是为了 ...

  3. 最强Java并发编程详解:知识点梳理,BAT面试题等

    本文原创更多内容可以参考: Java 全栈知识体系.如需转载请说明原处. 知识体系系统性梳理 Java 并发之基础 A. Java进阶 - Java 并发之基础:首先全局的了解并发的知识体系,同时了解 ...

  4. Javascript 异步加载详解

    Javascript 异步加载详解 本文总结一下浏览器在 javascript 的加载方式. 关键词:异步加载(async loading),延迟加载(lazy loading),延迟执行(lazy ...

  5. storm源码之理解Storm中Worker、Executor、Task关系 + 并发度详解

    本文导读: 1 Worker.Executor.task详解 2 配置拓扑的并发度 3 拓扑示例 4 动态配置拓扑并发度 Worker.Executor.Task详解: Storm在集群上运行一个To ...

  6. Mysql加锁过程详解(5)-innodb 多版本并发控制原理详解

    Mysql加锁过程详解(1)-基本知识 Mysql加锁过程详解(2)-关于mysql 幻读理解 Mysql加锁过程详解(3)-关于mysql 幻读理解 Mysql加锁过程详解(4)-select fo ...

  7. 【转】Python的hasattr() getattr() setattr() 函数使用方法详解

    Python的hasattr() getattr() setattr() 函数使用方法详解 hasattr(object, name)判断一个对象里面是否有name属性或者name方法,返回BOOL值 ...

  8. 【python库模块】Python subprocess模块功能与常见用法实例详解

    前言 这篇文章主要介绍了Python subprocess模块功能与常见用法,结合实例形式详细分析了subprocess模块功能.常用函数相关使用技巧. 参考 1. Python subprocess ...

  9. 利用python求解物理学中的双弹簧质能系统详解

    利用python求解物理学中的双弹簧质能系统详解 本文主要给大家介绍了关于利用python求解物理学中双弹簧质能系统的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 物理的 ...

随机推荐

  1. 查看SqlServer的内存使用情况

    上一篇提到动态T-SQL会产生较多的执行计划,这些执行计划会占用多少内存呢?今天从徐海蔚的书中找到了答案.动态视图不仅可以查到执行计划的缓存,数据表的页面缓存也可以查到,将SQL整理一下,做个标记. ...

  2. SQL SERVER 只有MDF文件的恢复

    方式一: .create a database same name as .mdf file; .Stop SQL Server; . recover .mdf file; . Start SQL S ...

  3. Andaroid L新特性

    1.Material Design”(材料设计)的全新设计理念,和Holo相比,Material Design更加色彩丰富,不像Holo那样灰暗 2.1)卡片风格(锁屏界面) 2)环动式设计  And ...

  4. du 命令,对文件和目录磁盘使用的空间的查看

    Linux du命令也是查看使用空间的,但是与df命令不同的是Linux du命令是对文件和目录磁盘使用的空间的查看,还是和df命令有一些区别的. 1.命令格式: du [选项][文件] 2.命令功能 ...

  5. Hibernate延迟加载机制详解

    摘自 http://blog.chinaunix.net/uid-20577907-id-3129234.html 1 延迟加载: 延迟加载机制是为了避免一些无谓的性能开销而提出来的,所谓延迟加载就是 ...

  6. Java与.NET兼容的RSA密钥持久化方法

    默认情况下,.NET生成的RSA密钥对可以用XML或字节流来保存,而JAVA中生成的RSA密钥对只能用字节流来保存.而它们的字节流格式不同,就导致Java中生成的RSA密钥对不能在.NET中使用,而. ...

  7. c# 中的多线程和异步

    前言: 1.异步和多线程有区别吗? 答案:多线程可以说是实现异步的一种方法方法,两者的共同目的:使主线程保持对用户操作的实时响应,如点击.拖拽.输入字符等.使主程序看起来实时都保持着等待用户响应的状态 ...

  8. 剑指offer系列55---最小的k个数

    [题目] 输入n个整数,找出其中最小的K个数.例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,. *[思路]排序,去除k后的数. package com.exe11 ...

  9. 【转】class卸载、热替换和Tomcat的热部署的分析

    这篇文章主要是分析Tomcat中关于热部署和JSP更新替换的原理,在此之前先介绍class的热替换和class的卸载的原理.一 class的热替换ClassLoader中重要的方法 loadClass ...

  10. 14款经典的MySQL客户端软件

    1. EMS MySQL Manager 强大的mysql管理工具,允许用户通过图形界面创建或编辑数据库对象,并提供通过sql语句管理用户和权限,通过图形界面建立sql语句,自动生成html格式的数据 ...