Python3: 生成器Generators詳解

三個部分

可迭代對象(Iterable)

Python中任意的對象,只要它定義了可以返回一個迭代器的__iter__方法,或者定義了可以支持下標索引的__getitem__方法,那麼它就是一個可迭代對象。

迭代器(Iterator)

任意對象,只要定義了next(Python2)或者__next__方法,它就是一個迭代器。

迭代(Iteration)

簡單來說,它就是從某個地方(比如列表)取出一個元素的過程。當使用一個循環來遍歷某個東西時,這個過程本身就叫迭代。

生成器也是一種迭代器,但是隻能對其迭代一次。這是因爲他們並沒有把所有的值存在內存中,而是在運行時生成值。通過遍歷來使用他們,要麼用一個“for”循環,要麼將它們傳遞給任意可以進行迭代的函數和結構。大多數時候生成器是以函數來實現的。然而,它們並不返回一個值,而是yield一個值。

# 簡單的例子
def generator_function():
    for i in range(10):
        yield i
for item in generator_function():
    print(item)
0
1
2
3
4
5
6
7
8
9

但生成器最佳的應用場景是:你不想同一時間將所有計算出來的大量結果集分配到內存當中,特別是結果集裏還包含循環。(這樣會消耗大量資源)

許多Python2裏的標準函數都會返回列表,而Python3都修改成了返回生成器,因爲生成器佔用更少的資源。

# 計算斐波那契數列的生成器
# 用這種方式,可以不用擔心它會使用大量資源
def fibon(n):
    a = b = 1
    for i in range(n):
        yield a
        a, b = b, a + b
for x in fibon(100):
    print(x)
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
6765
10946
17711
28657
46368
75025
121393
196418
317811
514229
832040
1346269
2178309
3524578
5702887
9227465
14930352
24157817
39088169
63245986
102334155
165580141
267914296
433494437
701408733
1134903170
1836311903
2971215073
4807526976
7778742049
12586269025
20365011074
32951280099
53316291173
86267571272
139583862445
225851433717
365435296162
591286729879
956722026041
1548008755920
2504730781961
4052739537881
6557470319842
10610209857723
17167680177565
27777890035288
44945570212853
72723460248141
117669030460994
190392490709135
308061521170129
498454011879264
806515533049393
1304969544928657
2111485077978050
3416454622906707
5527939700884757
8944394323791464
14472334024676221
23416728348467685
37889062373143906
61305790721611591
99194853094755497
160500643816367088
259695496911122585
420196140727489673
679891637638612258
1100087778366101931
1779979416004714189
2880067194370816120
4660046610375530309
7540113804746346429
12200160415121876738
19740274219868223167
31940434634990099905
51680708854858323072
83621143489848422977
135301852344706746049
218922995834555169026
354224848179261915075
# 不用生成器,計算很大時,可能會用盡所有的資源
def fibon(n):
    a = b = 1
    result =[]
    for i in range(n):
        result.append(a)
        a, b = b, a+b
    return result
f = fibon(100)
print(f)
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986, 102334155, 165580141, 267914296, 433494437, 701408733, 1134903170, 1836311903, 2971215073, 4807526976, 7778742049, 12586269025, 20365011074, 32951280099, 53316291173, 86267571272, 139583862445, 225851433717, 365435296162, 591286729879, 956722026041, 1548008755920, 2504730781961, 4052739537881, 6557470319842, 10610209857723, 17167680177565, 27777890035288, 44945570212853, 72723460248141, 117669030460994, 190392490709135, 308061521170129, 498454011879264, 806515533049393, 1304969544928657, 2111485077978050, 3416454622906707, 5527939700884757, 8944394323791464, 14472334024676221, 23416728348467685, 37889062373143906, 61305790721611591, 99194853094755497, 160500643816367088, 259695496911122585, 420196140727489673, 679891637638612258, 1100087778366101931, 1779979416004714189, 2880067194370816120, 4660046610375530309, 7540113804746346429, 12200160415121876738, 19740274219868223167, 31940434634990099905, 51680708854858323072, 83621143489848422977, 135301852344706746049, 218922995834555169026, 354224848179261915075]

在測試生成器使用一次迭代之前,先了解一下Python內置函數:next()

它允許獲取一個序列的下一個元素。

def generator_function():
    for i in range(3):
        yield i

gen = generator_function()
print(next(gen))
print(next(gen))
print(next(gen))
print(next(gen))
0
1
2



---------------------------------------------------------------------------

StopIteration                             Traceback (most recent call last)

<ipython-input-5-fc13b4ccc394> in <module>()
      7 print(next(gen))
      8 print(next(gen))
----> 9 print(next(gen))


StopIteration: 

在yield掉所有的值後,next()觸發一個StopIteration的異常。因爲所有的值都被yield完了。

但爲什麼使用for循環時沒有這個異常呢?

答案是:for循環會自動捕捉到這個異常並停止調用next()

# Python 中一些內置數據類型也支持迭代
my_String = "Python"
next(my_String)
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-6-587e901c8cc9> in <module>()
      1 # Python 中一些內置數據類型也支持迭代
      2 my_String = "Python"
----> 3 next(my_String)


TypeError: 'str' object is not an iterator

很明顯,str不是一個迭代器

# iter支持迭代的,它將根據一個可迭代對象返回一個迭代器對象
my_String = "Python"
my_Iter = iter(my_String)
next(my_Iter)
'P'
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章