Python列表生成式、迭代器、生成器
阿新 • • 發佈:2019-02-15
三元表示式
列表生成式
我們現在有個需求,將列表[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]中每個數字都加1,你能想到幾種解決方案?
NO1. 普通青年版
a = [1, 2, 3, 4, 5, 6, 7, 8, 9]
b = []
for i in a:
b.append(i + 1)
a = b
print("a = ", a)
a = [1, 2, 3, 4, 5, 6, 7, 8, 9]
for index, i in enumerate(a):
a[index] += 1
print("a = " , a)
NO2. 文藝青年版
a = [1, 2, 3, 4, 5, 6, 7, 8, 9]
a = map(lambda x: x + 1, a)
print("list[a] = ", list(a))
N03. 裝X青年版
a = [1, 2, 3, 4, 5, 6, 7, 8, 9]
a = [i + 1 for i in range(1, 10)]
print("a = ", a)
迭代器
迭代是訪問集合元素的⼀種⽅式。迭代器是⼀個可以記住遍歷的位置的物件。迭代器物件從集合的第⼀個元素開始訪問,直到所有的元素被訪問完結束。迭代器只能往前不會後退。
可迭代物件
- list、tuple、dict、set、str等
- generator,包括生成器和帶有yield的generator function
- 可直接作用於for迴圈的物件統稱為可迭代物件——Iterable
判斷物件是否可迭代
迭代器物件使用
迭代器的優缺點
- 優點
- 提供一種統一的、不依賴於索引的迭代方式
- 惰性計算,節省記憶體
- 缺點
- 無法獲取長度(只有在next完畢才知道到底有幾個值)
- 一次性的,只能往後走,不能往前退
生成器
What is 生成器?
通過列表⽣成式,我們可以直接建立⼀個列表。但是,受到記憶體限制,列表容量肯定是有限的。⽽且,建立⼀個包含100萬個元素的列表,不僅佔⽤很⼤的儲存空間,如果我們僅僅需要訪問前⾯⼏個元素,那後⾯絕⼤多數元素佔⽤的空間都⽩⽩浪費了。所以,如果列表元素可以按照某種演算法推算出來,那我們是否可以在迴圈的過程中不斷推算出後續的元素呢?這樣就不必建立完整的list,從⽽節省⼤量的空間。在Python中,這種⼀邊迴圈⼀邊計算的機制,稱為⽣成器:generator。
NO1. 將一個列表生成式的[]改成(),就建立了一個generator
>>> L = [x ** 2 for x in range(5)]
>>> L
[0, 1, 4, 9, 16]
>>>
>>> G = (x ** 2 for x in range(5))
>>> G
<generator object <genexpr> at 0x104516828>
>>> G.__next__()
0
>>> next(G)
1
>>> for i in G:
... print(i)
...
4
9
16
NO2. generator⾮常強⼤。如果推算的演算法⽐較複雜,⽤類似列表⽣成式的 for 迴圈⽆法實現的時候,還可以⽤函式來實現。
⽐如,著名的斐波拉契數列(Fibonacci),除第⼀個和第⼆個數外,任意⼀個數都可由前兩個數相加得到
1, 1, 2, 3, 5, 8, 13, 21, 34, …
斐波拉契數列⽤列表⽣成式寫不出來,但是,⽤函式把它打印出來卻很容易。
仔細觀察,可以看出,fib函式實際上是定義了斐波拉契數列的推算規則,可以從第⼀個元素開始,推算出後續任意的元素,這種邏輯其實⾮常類似generator。
也就是說,上⾯的函式和generator僅⼀步之遙。要把fib函式變成generator,只需要把print(b)改為yield b就可以了。
In [4]: def fib(max):
...: n = 0
...: a = 0
...: b = 1
...: while n < max:
...: yield b
...: a = b
...: b = a + b
...: n += 1
...: return 'done'
...:
...:
In [5]: f = fib(5)
In [6]: next(f)
Out[6]: 1
In [7]: next(f)
Out[7]: 2
In [8]: next(f)
Out[8]: 4
In [9]: next(f)
Out[9]: 8
In [10]: next(f)
Out[10]: 16
In [11]: next(f)
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-11-aff1dd02a623> in <module>()
----> 1 next(f)
StopIteration: done
但是⽤for迴圈調⽤generator時,發現拿不到generator的return語句的返回值。如果想要拿到返回值,必須捕獲StopIteration錯誤,返回值包含在StopIteration的value中。
只要函式內部包含有yield關鍵字,那麼函式名()的到的結果就是生成器,並且不會執行函式內部程式碼。
自定義函式模擬range(1,7,2)
send()
def eater(name):
print('%s 準備開始吃飯啦' % name)
my_food = None
while True:
food = yield None
print('%s 吃了 %s' % (name, food))
g = eater('Allen')
print(next(g))
g.send(None) #對於表示式形式的yield,在使用時,第一次必須傳None,g.send(None)等同於next(g)
g.send('蒸羊羔')
g.send('蒸鹿茸')
g.send('蒸熊掌')
g.send('燒素鴨')
#g.close()
g.send('燒素鵝')
g.send('燒鹿尾')