Python關鍵字yield詳解以及Iterable 和Iterator區別

迭代器(Iterator)html

爲了理解yield是什麼,首先要明白生成器(generator)是什麼,在講生成器以前先說說迭代器(iterator),當建立一個列表(list)時,你能夠逐個的讀取每一項,這就叫作迭代(iteration)。java

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

Mylist就是一個迭代器,不論是使用複雜的表達式列表,仍是直接建立一個列表,都是可迭代的對象。python

  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,這些迭代對象很是方便咱們使用,由於你能夠按照你的意願進行重複的讀取。可是你不得不預先存儲全部的元素在內存中,那些對象裏有不少元素時,並非每一項都對你有用。api

生成器(Generators)oracle

生成器一樣是可迭代對象,可是你只能讀取一次,由於它並無把全部值存放內存中,它動態的生成值:ide

  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在告訴程序,要求函數返回一個生成器。ui

  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)生成器的時候纔會運行。this

函數第一次運行時,它會從函數開始處直到碰到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/

相關文章
相關標籤/搜索