1. 程式人生 > >Python列表生成式、迭代器、生成器

Python列表生成式、迭代器、生成器

三元表示式

列表生成式

我們現在有個需求,將列表[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)

迭代器

迭代是訪問集合元素的⼀種⽅式。迭代器是⼀個可以記住遍歷的位置的物件。迭代器物件從集合的第⼀個元素開始訪問,直到所有的元素被訪問完結束。迭代器只能往前不會後退。

可迭代物件

  1. list、tuple、dict、set、str等
  2. generator,包括生成器和帶有yield的generator function
  3. 可直接作用於for迴圈的物件統稱為可迭代物件——Iterable

判斷物件是否可迭代

迭代器物件使用

迭代器的優缺點

  1. 優點
    • 提供一種統一的、不依賴於索引的迭代方式
    • 惰性計算,節省記憶體
  2. 缺點
    • 無法獲取長度(只有在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('燒鹿尾')