迭代器(Iterator)
爲了理解yield是什麼,首先要明白生成器(generator)是什麼,在講生成器之前先說說迭代器(iterator),當創建一個列表(list)時,你可以逐個的讀取每一項,這就叫做迭代(iteration)。
- mylist = [ 1 , 2 , 3 ]
- for i in mylist :
- print (i)
- 1
- 2
- 3
Mylist就是一個迭代器,不管是使用複雜的表達式列表,還是直接創建一個列表,都是可迭代的對象。
- mylist = [x*x for x in range( 3 )]
- for i in mylist :
- print (i)
- 0
- 1
- 4
你可以使用“for··· in ···”來操作可迭代對象,如:list,string,files,這些迭代對象非常方便我們使用,因爲你可以按照你的意願進行重複的讀取。但是你不得不預先存儲所有的元素在內存中,那些對象裏有很多元素時,並不是每一項都對你有用。
生成器(Generators)
生成器同樣是可迭代對象,但是你只能讀取一次,因爲它並沒有把所有值存放內存中,它動態的生成值:
- mygenerator = (x*x for x in range( 3 ))
- for i in mygenerator :
- print (i)
- 0
- 1
- 4
使用()和[]結果是一樣的,但是,第二次執行“ for in mygenerator”不會有任何結果返回,因爲它只能使用一次。首先計算0,然後計算1,之後計算4,依次類推。
Yield
Yield是關鍵字, 用起來像return,yield在告訴程序,要求函數返回一個生成 器。
- def createGenerator() :
- mylist = range( 3 )
- for i in mylist :
- yield i*i
- mygenerator = createGenerator() # create a generator
- print (mygenerator) # mygenerator is an object!
- < generator object createGenerator at 0xb7555c34 >
- for i in mygenerator:
- print (i)
- 0
- 1
- 4
這個示例本身沒什麼意義,但是它很清晰地說明函數將返回一組僅能讀一次的值,要想掌握yield,首先必須理解的是:當你調用生成器函數的時候,如上例中的createGenerator(),程序並不會執行函數體內的代碼,它僅僅只是返回生成器對象,這種方式頗爲微妙。函數體內的代碼只有直到每次循環迭代(for)生成器的時候纔會運行。
函數第一次運行時,它會從函數開始處直到碰到yield時,就返回循環的第一個值,然後,交互的運行、返回,直到沒有值返回爲止。如果函數在運行但是並沒有遇到yield,就認爲該生成器是空,原因可能是循環終止,或者沒有滿足任何”if/else”。
接下來讀一小段代碼來理解生成器的優點:
控制生成器窮舉
- >>> class Bank(): # 創建銀行,構造ATM機
- ... crisis = False
- ... def create_atm( self ) :
- ... while not self .crisis :
- ... yield "$100"
- >>> hsbc = Bank() # 沒有危機時,你想要多少,ATM就可以吐多少
- >>> corner_street_atm = hsbc.create_atm()
- >>> print (corner_street_atm.next())
- $ 100
- >>> print (corner_street_atm.next())
- $ 100
- >>> print ([corner_street_atm.next() for cash in range( 5 )])
- [ '$100' , '$100' , '$100' , '$100' , '$100' ]
- >>> hsbc.crisis = True # 危機來臨,銀行沒錢了
- >>> print (corner_street_atm.next())
- <type 'exceptions.StopIteration' >
- >>> wall_street_atm = hsbc.ceate_atm() # 新建ATM,銀行仍然沒錢
- >>> print (wall_street_atm.next())
- <type 'exceptions.StopIteration' >
- >>> hsbc.crisis = False # 麻煩就是,即使危機過後銀行還是空的
- >>> print (corner_street_atm.next())
- <type 'exceptions.StopIteration' >
- >>> brand_new_atm = hsbc.create_atm() # 構造新的ATM,恢復業務
- >>> for cash in brand_new_atm :
- ... print cash
- $ 100
- $ 100
- $ 100
- $ 100
- $ 100
- $ 100
- $ 100
- $ 100
- $ 100
對於訪問控制資源,生成器顯得非常有用。
迭代工具,你最好的朋友
迭代工具模塊包含了操做指定的函數用於操作迭代器。 想複製一個迭代器出來?鏈接兩個迭代器?以one liner(這裏的one-liner只需一行代碼能搞定的任務)用內嵌的列表組合一組值?不使用list創建Map/Zip?···,你要做的就是 import itertools,舉個例子吧:
四匹馬賽跑到達終點排名的所有可能性:
- >>> horses = [ 1 , 2 , 3 , 4 ]
- >>> races = itertools.permutations(horses)
- >>> print (races)
- <itertools.permutations object at 0xb754f1dc >
- >>> print (list(itertools.permutations(horses)))
- [( 1 , 2 , 3 , 4 ),
- ( 1 , 2 , 4 , 3 ),
- ( 1 , 3 , 2 , 4 ),
- ( 1 , 3 , 4 , 2 ),
- ( 1 , 4 , 2 , 3 ),
- ( 1 , 4 , 3 , 2 ),
- ( 2 , 1 , 3 , 4 ),
- ( 2 , 1 , 4 , 3 ),
- ( 2 , 3 , 1 , 4 ),
- ( 2 , 3 , 4 , 1 ),
- ( 2 , 4 , 1 , 3 ),
- ( 2 , 4 , 3 , 1 ),
- ( 3 , 1 , 2 , 4 ),
- ( 3 , 1 , 4 , 2 ),
- ( 3 , 2 , 1 , 4 ),
- ( 3 , 2 , 4 , 1 ),
- ( 3 , 4 , 1 , 2 ),
- ( 3 , 4 , 2 , 1 ),
- ( 4 , 1 , 2 , 3 ),
- ( 4 , 1 , 3 , 2 ),
- ( 4 , 2 , 1 , 3 ),
- ( 4 , 2 , 3 , 1 ),
- ( 4 , 3 , 1 , 2 ),
- ( 4 , 3 , 2 , 1 )]
理解迭代的內部機制:
迭代(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則不然,每次調用都會返回一個從頭開始計數的迭代器。
多個迭代器是互不干擾的