【python實用特性】- yield生成器


這個概念較難理解,我們可以通過解答以下幾個問題來掌握它。
本節會用到可迭代對象列表生成器知識,建議先看下之前的文章進行了解。
【python實用特性】- 迭代、可迭代對象、迭代器
【python實用特性】- 列表生成式

1、什麼是生成器?

生成器: 生成,很好理解,創建一個對象的意思。那是什麼呢?器可以理解爲工具的意思,所以生成器,就是按照一定規則,創建某種對象的工具。

2、爲何要用生成器?有何好處?

舉個簡單的例子,我們要創建一個100個元素的列表。很簡單,list(range(100)),那如果要創建一個100萬、1000萬元素的列表呢?
誠然,我們依舊可以使用list(range(10000*100)list(range(10000*1000)來達到目的。
但這樣會帶來幾個問題:

  • 變量存儲於內存中,所以列表長度受限於內存大小,是有限的
  • 列表需要將所有元素一次性創建完成後才能操作,數據過大時,內存使用會瞬間升幅,存在內存不足風險
  • 大多數時候只需要用到部分元素,這就造成了內存空間的浪費
    爲了解決以上問題,於是引入了一種不用一次性全部生成,可以按需逐次獲取數據的方案——生成器
    所以使用生成器的好處也很明顯:
    (1)不用一次性生成所有數據,能有效節約內存
    (2)可以按需逐次獲取數據,不會造成內存空間的浪費
3、python中如何實現生成器?

一般來說,python中的生成器有兩種實現方式。

  • 普通實現
    將列表生成式的[]改爲()即可,實例如下:

    g = (x for x in range(10))
    print(type(g)) #type查看g的類型
    from typing import Iterable
    print(isinstance(g,Iterable)) #判斷是否是可迭代對象
    

    輸出:

    <class 'generator'>
    True
    

    可見,此時的g便是一個生成器,同時也是一個可迭代對象!
    這意味着我們可以使用 next(g)進行 取值

  • yield實現
    實例如下,打印0~2

    #自然數生成器
    def getN(N):
        n = 0
        while n<N:
            yield n  #使用yield 返回數據
            n+=1
    g = getN(3)
    print(next(g))
    print(next(g))
    print(next(g))
    

    輸出:

    0
    1
    2
    

    如上,我們在函數 getN(N)中使用了yield關鍵字返回數據,所以這個函數就變成了一個生成器。
    小結: 若一個函數中使用了yield返回數據,那這個函數就是一個生成器。

4、yield生成器的運行機制

通過上面的學習,我們已經知道如何去構造一個生成器。那它是如何運行的呢?又爲何說它是逐次獲取數據呢?
先看一個例子

def test():
    print('返回第一個數據:')
    yield 'data1'
    print('返回第二個數據:')
    yield 'data2'
    print('返回第三個數據:')
    yield 'data3'
g = test()
print(next(g))

輸出:

返回第一個數據:
data1

可見,有三個yield 返回,但因爲我們只使用了一次 next()函數,所以只打印了data1,第一個yield返回的數據。
那調用兩次呢?

print(next(g))
print(next(g))

輸出:

返回第一個數據:
data1
返回第二個數據:
data2

小結: 調用一次 next()函數,便執行一次生成器,遇到yield關鍵字後,將數據返回。之後生成器暫停運行,保存當前的運行狀態,等待下一個next()調用。

5、使用循環來迭代生成器

上面的例子中,我們都是通過next()函數來取值。這種方式當數據量大了之後,就會變得很麻煩。所以需要使用更高效的方法,而因爲生成器也是一個可迭代對象,所以我們可以使用循環來進行迭代。
打印0~2的實例改寫如下:

  • while循環

    
    def getN(N):
        n = 0
        while n<N:
            yield n
            n+=1
    g = getN(10)
    while True:
        try:
            print(next(g),end='\t')
        except StopIteration:
            break #跳出循環
    

    注:使用while循環實現時,要用try...except捕獲StopIterator異常。

  • for循環

    def getN(N):
        n = 0
        while n<N:
            yield n
            n+=1
    g = getN(3)
    for data in g:
        print(data)
    

    注:for循環會自動調用next()函數,且捕獲異常。推薦使用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章