第6章 函式
- 6.1 函式的定義和呼叫
- 6.2 引數傳遞
- 6.3 函式返回值
- 6.4 變數作用域
- 6.5 匿名函式(lambda)
- 6.6 遞迴函式
- 6.7 迭代器
- 6.8 生成器
- 6.9 裝飾器
6.8 生成器
看看廖雪峰大神的解釋:
通過列表生成式,我們可以直接建立一個列表。但是,受到記憶體限制,列表容量肯定是有限的。
而且,建立一個包含100萬個元素的列表,不僅佔用很大的儲存空間,如果我們僅僅需要訪問前面幾個元素,那後面絕大多數元素佔用的空間都白白浪費了。
所以,如果列表元素可以按照某種演算法推算出來,那我們是否可以在迴圈的過程中不斷推算出後續的元素呢?
這樣就不必建立完整的list,從而節省大量的空間。
在Python中,這種一邊迴圈一邊計算的機制,稱為生成器(Generator)。
生成器generator,也是一種迭代器,但是你只能對其迭代一次。這是因為它們並沒有把所有的值存在記憶體中,而是在執行時生成值。
生成器generator物件是一種特殊的迭代器iterator函式,它會在執行過程中儲存執行的上下文環境,並在下次迴圈中從yield語句後繼續執行,生成器的標誌就是yield關鍵字。
generator不需要丟擲StopIteration異常(你可以看做yield已經在內部實現了StopIteration跳出迴圈),函式並沒有將序列項一次生成,所以generator在實現上可以有無窮個元素,而不需要無窮的儲存空間,這在記憶體優化方面很有用處。
使用isinstance(實體名,Generator)可判斷是否為生成器。
# 驗證下一個列表是否為可迭代物件Iterable、迭代器Iterator、生成器Generator
from collections.abc import Iterator, Iterable, Generator
province = ['Guangdong', 'HuNan', 'JiangSu', 'HeNan', 'HeBei']
print(isinstance(province, Iterator), isinstance(province, Iterable), isinstance(province, Generator))
output:
False True False
# 從結果來看,一個列表是可迭代物件但不是迭代器,也不是生成器
你通過遍歷來使用它們,要麼用一個for迴圈,要麼將它們傳遞給任意可以進行迭代的函式和結構。大多數時候生成器是以函式來實現的。然而,它們並不返回一個值,而是yield(暫且譯作“生出”)一個值。
生成器的建立辦法有兩種:
- 通過函式建立,稱作生成器函式generator function
- 通過推導式建立,例如g=(x*2 for x in range(10)),稱作生成器表示式generator expression
每次對生成器呼叫 next() 時,它會從上次離開位置恢復執行(它會記住上次執行語句時的所有資料值)。顯示如何非常容易地建立生成器的示例如下:
def reverse(data):
for index in range(len(data)-1, -1, -1):
yield data[index]
>>> for char in reverse('golf'):
... print(char)
...
f
l
o
g
可以用生成器來完成的操作同樣可以用前一節所描述的基於類的迭代器來完成。但生成器的寫法更為緊湊,因為它會自動建立 iter() 和 next()方法。
生成器表示式generator expression
生成器不一定要用複雜的函式表示,python提供了簡潔的生成器表示式。
從形式上來看,生成器表示式和列表推導式很像,僅僅是將列表推導式中的[]替換為(),但是兩者差別挺大,生成器表示式可以說組合了迭代功能和列表解析功能。
生成器表示式可以認為是一種特殊的生成器函式,類似於lambda表示式和普通函式。但是和生成器一樣,生成器表示式也是返回生成器generator物件,一次只返回一個值。
# 生成器表示式
g = (x*2 for x in range(4))
print(type(g))
print(next(g))
print(next(g))
print(next(g))
print(next(g))
f = (i*i for i in range(10))
print(type(f))
print(next(f))
print(next(f))
print(next(f))
print(next(f))
output:
<class 'generator'>
0
2
4
6
<class 'generator'>
0
1
4
9