目錄
容器(container)
迭代器(iterator)
生成器(generator)
生成器簡介
yield案例
容器是一種把多個元素組織在一起的數據結構,容器中的元素可以逐個地迭代獲取,可以用in, not in關鍵字判斷元素是否包含在容器中。
在Python中,常見的容器對象有:
1) list, deque, ….
2) set, frozensets, ….
3) dict, defaultdict, OrderedDict, Counter, ….
4) tuple, namedtuple, …
5) str
容器比較容易理解,因為可以把它看作是一個盒子,裡面可以塞任何東西。從技術角度來說,當它可以用來詢問某個元素是否包含在其中時,那麼這個對象就可以認為是一個容器,比如 list,dict, set,tuples都是容器對象。
容器是一系列元素的集合,str、list、set、dict、file、sockets對象都可以看作是容器,容器都可以被迭代(用在for,while等語句中),因此他們被稱為可迭代對象。但凡是可以返回一個迭代器的對象都可稱之為可迭代對象,
那麼什麼迭代器呢?它是一個帶狀態的對象,能在你調用next()方法的時候返回容器中的下一個值,任何實現了__iter__和__next__()方法的對象都是迭代器,__iter__返回迭代器自身,__next__返回容器中的下一個值,如果容器中沒有更多元素了,則拋出StopIteration異常,至於它們到底是如何實現的這並不重要。迭代器就是實現了工廠模式的對象,它在每次詢問要下一個值的時候給你返
生成器算得上是Python語言中最吸引人的特性之一,生成器其實是一種特殊的迭代器,不過這種迭代器更加優雅。它不需要再像上面的類一樣寫__iter__()和__next__()方法了,只需要一個yiled關鍵字。
生成器一定是迭代器(反之不成立),因此任何生成器也是以一種懶加載的模式生成值。它特殊的地方在於函數體中沒有return關鍵字,函數的返回值是一個生成器對象,只有顯示或隱示地調用next的時候才會真正執行裡面的代碼。
生成器在Python中是一個非常強大的編程結構,可以用更少地中間變量寫流式代碼,相比其它容器對象它更能節省內存和CPU,當然它可以用更少的代碼來實現相似的功能。
生成器對象是一個迭代器:但是它比迭代器對象多了一些方法,它們包括send方法,throw方法和close方法。這些方法,主要是用於外部與生成器對象的交互。
send方法有一個參數:該參數指定的是上一次被掛起的yield語句的返回值。
案例如下:
def MyGenerator():
receive = yield 100
print('extra' + str(receive))
receive = yield 200
print('extra' + str(receive))
receive = yield 300
print('extra' + str(receive))
gen = MyGenerator()
print(next(gen)) #也可以用:print(gen.send(None))
print("first")
print(gen.send(2))
print("second")
print(gen.send(3))
print("third")
輸出:100 first extra2 200 second extra3 300 third
注意,yield語句是最具魔力的表達式:這裡“yield 100”整體被視為一個表達式,send的內容會作為這個表達式的值,隨便左邊用什麼變量接收或者不接收,總之yield就是send進來的那個值。這個表達式變成send進來後的那個值之後,繼續往下執行,直到再次遇到yield,掛起。
如何啟動生成器呢?調用(gen.next()或gen.send(None))上面代碼的運行過程如下。
當調用gen.next()方法時:python首先會執行MyGenerator方法的yield 1語句。由於是一個yield語句,因此方法的執行過程被掛起,而next方法返回值為yield關鍵字後面表達式的值,即為100。
當調用gen.send(2)方法時:python首先恢復MyGenerator方法的運行環境。同時,將表達式(yield 100)的返回值定義為send方法參數的值,即為2。這樣,接下來value=(yield 100)這一賦值語句會將value的值設為2。繼續運行會遇到yield value語句,MyGenerator方法再次被掛起。同時,send方法的返回值為yield關鍵字後面表達式的值,也即value的值,為200。
當調用gen.send(3)方法時:首先恢復MyGenerator方法的運行環境。同時,將表達式(yield value)的返回值定義為send方法參數的值,即為3。這樣,接下來value=(yield 200)這一賦值語句會將value的值置為3。繼續運行,MyGenerator方法執行完畢。以此類推….
總的來說:
send方法和next方法唯一的區別,在執行send方法會首先把上一次掛起的yield語句的返回值通過參數設定,從而實現與生成器方法的交互。但是需要注意,在一個生成器對象沒有執行next方法之前,由於沒有yield語句被掛起,所以執行send方法會報錯。當然,下面的代碼是可以接受的:
gen = MyGenerator()
print( gen.send(None) )
因為當send方法的參數為None時,它與next方法完全等價。但是注意,雖然上面的代碼可以接受,但是不規范。所以,在調用send方法之前,還是先調用一次next方法為好。