• Python关键字yield详解以及Iterable 和Iterator区别


    迭代器(Iterator)

    为了理解yield是什么,首先要明白生成器(generator)是什么,在讲生成器之前先说说迭代器(iterator),当创建一个列表(list)时,你可以逐个的读取每一项,这就叫做迭代(iteration)。

    1. mylist = [123]   
    2.  for i in mylist :   
    3.  print(i)   
    4. 1 
    5. 2 
    6. 3 

    Mylist就是一个迭代器,不管是使用复杂的表达式列表,还是直接创建一个列表,都是可迭代的对象。

    1. mylist = [x*x for x in range(3)]   
    2. for i in mylist :   
    3. print(i)   
    4. 0 
    5. 1 
    6. 4 

    你可以使用“for··· in ···”来操作可迭代对象,如:list,string,files,这些迭代对象非常方便我们使用,因为你可以按照你的意愿进行重复的读取。但是你不得不预先存储所有的元素在内存中,那些对象里有很多元素时,并不是每一项都对你有用。

    生成器(Generators)

    生成器同样是可迭代对象,但是你只能读取一次,因为它并没有把所有值存放内存中,它动态的生成值:

    1. mygenerator = (x*x for x in range(3))   
    2. for i in mygenerator :   
    3. print(i)   
    4. 0 
    5. 1 
    6. 4 

    使用()和[]结果是一样的,但是,第二次执行“ for in mygenerator”不会有任何结果返回,因为它只能使用一次。首先计算0,然后计算1,之后计算4,依次类推。

    Yield

    Yield是关键字, 用起来像return,yield在告诉程序,要求函数返回一个生成器。

    1. def createGenerator() :   
    2. mylist = range(3)   
    3. for i in mylist :   
    4. yield i*i   
    5.     
    6. mygenerator = createGenerator() # create a generator   
    7. print(mygenerator) # mygenerator is an object!   
    8. <generator object createGenerator at 0xb7555c34>   
    9. for i in mygenerator:   
    10. print(i)   
    11. 0 
    12. 1 
    13. 4 

    这个示例本身没什么意义,但是它很清晰地说明函数将返回一组仅能读一次的值,要想掌握yield,首先必须理解的是:当你调用生成器函数的时候,如上例中的createGenerator(),程序并不会执行函数体内的代码,它仅仅只是返回生成器对象,这种方式颇为微妙。函数体内的代码只有直到每次循环迭代(for)生成器的时候才会运行。

    函数第一次运行时,它会从函数开始处直到碰到yield时,就返回循环的第一个值,然后,交互的运行、返回,直到没有值返回为止。如果函数在运行但是并没有遇到yield,就认为该生成器是空,原因可能是循环终止,或者没有满足任何”if/else”。

    接下来读一小段代码来理解生成器的优点:

    控制生成器穷举

    1. >>> class Bank(): # 创建银行,构造ATM机   
    2. ...    crisis = False 
    3. ...    def create_atm(self) :   
    4. ...        while not self.crisis :   
    5. ...            yield "$100" 
    6. >>> hsbc = Bank() # 没有危机时,你想要多少,ATM就可以吐多少   
    7. >>> corner_street_atm = hsbc.create_atm()   
    8. >>> print(corner_street_atm.next())   
    9. $100 
    10. >>> print(corner_street_atm.next())   
    11. $100 
    12. >>> print([corner_street_atm.next() for cash in range(5)])   
    13. ['$100''$100''$100''$100''$100']   
    14. >>> hsbc.crisis = True # 危机来临,银行没钱了   
    15. >>> print(corner_street_atm.next())   
    16. <type 'exceptions.StopIteration'>   
    17. >>> wall_street_atm = hsbc.ceate_atm() # 新建ATM,银行仍然没钱   
    18. >>> print(wall_street_atm.next())   
    19. <type 'exceptions.StopIteration'>   
    20. >>> hsbc.crisis = False # 麻烦就是,即使危机过后银行还是空的   
    21. >>> print(corner_street_atm.next())   
    22. <type 'exceptions.StopIteration'>   
    23. >>> brand_new_atm = hsbc.create_atm() # 构造新的ATM,恢复业务   
    24. >>> for cash in brand_new_atm :   
    25. ...    print cash   
    26. $100 
    27. $100 
    28. $100 
    29. $100 
    30. $100 
    31. $100 
    32. $100 
    33. $100 
    34. $100 

    对于访问控制资源,生成器显得非常有用。

    迭代工具,你最好的朋友

    迭代工具模块包含了操做指定的函数用于操作迭代器。想复制一个迭代器出来?链接两个迭代器?以one liner(这里的one-liner只需一行代码能搞定的任务)用内嵌的列表组合一组值?不使用list创建Map/Zip?···,你要做的就是 import itertools,举个例子吧:

    四匹马赛跑到达终点排名的所有可能性:

    1. >>> horses = [1234]   
    2. >>> races = itertools.permutations(horses)   
    3. >>> print(races)   
    4. <itertools.permutations object at 0xb754f1dc>   
    5. >>> print(list(itertools.permutations(horses)))   
    6. [(1234),   
    7.  (1243),   
    8.  (1324),   
    9.  (1342),   
    10.  (1423),   
    11.  (1432),   
    12.  (2134),   
    13.  (2143),   
    14.  (2314),   
    15.  (2341),   
    16.  (2413),   
    17.  (2431),   
    18.  (3124),   
    19.  (3142),   
    20.  (3214),   
    21.  (3241),   
    22.  (3412),   
    23.  (3421),   
    24.  (4123),   
    25.  (4132),   
    26.  (4213),   
    27.  (4231),   
    28.  (4312),   
    29.  (4321)] 

    理解迭代的内部机制:

    迭代(iteration)就是对可迭代对象(iterables,实现了__iter__()方法)和迭代器(iterators,实现了__next__()方法)的一个操作过程。可迭代对象是任何可返回一个迭代器的对象,迭代器是应用在迭代对象中迭代的对象,换一种方式说的话就是:iterable对象的__iter__()方法可以返回iterator对象,iterator通过调用next()方法获取其中的每一个值(译者注),读者可以结合Java API中的 Iterable接口和Iterator接口进行类比。

    java Iterable接口:

    public interface Iterable<T>

    Implementing this interface allows an object to be the target of the "foreach" statement.

    方法:

    Iterator<T> iterator()
    Returns an iterator over a set of elements of type T.
    Returns:
    an Iterator.
    Iterator接口:
    public interface Iterator<E>

    An iterator over a collection. Iterator takes the place of Enumeration in the Java collections framework. Iterators differ from enumerations in two ways:

    • Iterators allow the caller to remove elements from the underlying collection during the iteration with well-defined semantics.
    • Method names have been improved.

    This interface is a member of the Java Collections Framework.

    boolean hasNext()
       Returns true if the iteration has more elements.
    E next()
      Returns the next element in the iteration.
    void remove()
      Removes from the underlying collection the last element returned by the iterator (optional operation).

    为什么一定要去实现Iterable这个接口呢? 为什么不直接实现Iterator接口呢?

    看一下JDK中的集合类,比如List一族或者Set一族, 
    都是实现了Iterable接口,但并不直接实现Iterator接口。 
    仔细想一下这么做是有道理的。因为Iterator接口的核心方法next()或者hasNext() 
    是依赖于迭代器的当前迭代位置的。 
    如果Collection直接实现Iterator接口,势必导致集合对象中包含当前迭代位置的数据(指针)。 
    当集合在不同方法间被传递时,由于当前迭代位置不可预置,那么next()方法的结果会变成不可预知。 
    除非再为Iterator接口添加一个reset()方法,用来重置当前迭代位置。 
    但即时这样,Collection也只能同时存在一个当前迭代位置。 
    而Iterable则不然,每次调用都会返回一个从头开始计数的迭代器。 
    多个迭代器是互不干扰的

    英文原文:The Python yield keyword explained

    原文链接:http://blog.jobbole.com/32748/

  • 相关阅读:
    SQL 分组 行变列的一个例子
    用JS如何獲得DropDownList所選Text和Value?
    用了.net2.0,再用1.1的问题。1.1里修改.cs文件不重新编译,.dll不重新生成。
    ASP.NET中上传文件
    获得用户控件的值!
    在没有vs2005环境里部署Crystal Reports 10水晶报表
    onkeypress,onkeydown,onkeyup区别
    2012湖南大学第八届程序设计竞赛 Incredible[公式]
    POJ3624 Charm Bracelet[01背包问题入门]
    HDOJ1257 最少拦截系统[DP入门]
  • 原文地址:https://www.cnblogs.com/youxin/p/3201983.html
Copyright © 2020-2023  润新知