Python 速查手冊

3.10 yield 運算

關鍵字 (keyword) yield 用在函數中對呼叫方產生數值,這是說函數 (function) 若使用 yield 產生數值的話,函數會回傳一個產生器 (generator) 物件 (object) ,可由 __next__() 方法 (method) 或內建函數 (built-in function) next() 依序取得 yield 產生的數值,注意 __next__() 方法前後被兩條底線包圍。這樣的執行方式,就好像呼叫函數後,函數並不會立即直接結束,而是與呼叫方同時執行的。

先來看個簡單的例子,函數 yield5() 只有簡單利用關鍵字 yield 產生整數 5 ,變數 (variable) a 會取得 yield5() 回傳的產生器物件,然後利用內建函數 next() 先取得變數 a 產生的第一個數值,就是整數 5 ,再用 next() 一次,結果發生 StopIteration 的錯誤,因為變數 a 中已經沒有數值可以產生了

def yield5():
    yield 5

a = yield5()
print(next(a))
print(next(a))

#《程式語言教學誌》的範例程式
# https://kaiching.org/
# 檔名:yield01.py
# 功能:示範 yield 運算
# 作者:張凱慶

於命令列執行以上程式,結果如下

$ python3 yield01.py
5
Traceback (most recent call last):
  File "yield01.py", line 6, in <module>
    print(next(a))
StopIteration
$

再來用 new_range() 簡單模擬內建函數 range() 的效果,此例變數 b 會是具有從 09 的產生器物件,這邊用內建函數 next() 取得變數 b 產生的整數兩次

def new_range(n):
    i = 0
    while i < n:
        yield i
        i += 1

b = new_range(10)
print(next(b))
print(next(b))

#《程式語言教學誌》的範例程式
# https://kaiching.org/
# 檔名:yield02.py
# 功能:示範 yield 運算
# 作者:張凱慶

於命令列執行以上程式,結果如下

$ python3 yield02.py
0
1
$

for 迴圈 (loop) 可以印出 new_range() 產生的所有整數,例如

def new_range(n):
    i = 0
    while i < n:
        yield i
        i += 1

for i in new_range(10):
    print(i)

#《程式語言教學誌》的範例程式
# https://kaiching.org/
# 檔名:yield03.py
# 功能:示範 yield 運算
# 作者:張凱慶

於命令列執行以上程式,結果如下

$ python3 yield03.py
0
1
2
3
4
5
6
7
8
9
$

關鍵字 return 是在函數中回傳數值, returnyield 的作用完全不同, return 後接的回傳值就是給呼叫方的物件,回傳值 (return value) 是什麼型態,呼叫方就得到什麼型態的物件,例如這裡定義了 fib() 函數,這是用來計算費氏數列的函數,結果回傳串列 (list) ,費氏數列則儲存在串列之中。

def fib(n):
    L = []
    i, a, b = 0, 0, 1
    while i < n:
        L.append(b)
        a, b = b, a + b
        i += 1
    
    return L

print(fib(10))

#《程式語言教學誌》的範例程式
# https://kaiching.org/
# 檔名:yield04.py
# 功能:示範 yield 運算
# 作者:張凱慶

於命令列執行以上程式,結果如下

$ python3 yield04.py
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
$

那函數用 returnyield 有什麼不同呢?這裡的 fib2() 函數用 yield 產生費氏數列,相同參數得到的結果跟 fib() 一樣,差別是 fib2() 回傳的是產生器物件,而非串列,利用產生器物件的好處是節省記憶體空間,並且提升程式執行的效率,這優點在資料量極少的情況下看不出來,可是一旦資料量暴增,例如串列中需要儲存數十到數百萬筆資料的時候,相對產生器物件需要的記憶體空間就相當少,所以當資料是依序計算取得的話,利用產生器物件就比較適合

def fib2(n):
    i, a, b = 0, 0, 1
    while True:
        if n <= 0 or i == n:
            break
        a, b = b, a + b
        yield a
        i += 1

d = fib2(10)
for i in d:
    print(i)

#《程式語言教學誌》的範例程式
# https://kaiching.org/
# 檔名:yield05.py
# 功能:示範 yield 運算
# 作者:張凱慶

於命令列執行以上程式,結果如下

$ python3 yield05.py
1
1
2
3
5
8
13
21
34
55
$

上一頁: 3.9 lambda 運算
Python 速查手冊 - 目錄
下一頁: 3.11 串列的綜合運算
回 Python 教材首頁