之(二) 生成器

一. 可迭代對象(Iterable) 和迭代器(Iterator)

Python中任意的對象, 只要它定義了可以返回一個迭代器的iter方法, 或者定義了可以支持下標索引的getitem方法, 那麼它就是一個可迭代對象. 簡單說, 可迭代對象就是能提供迭代器的任意對象. 那迭代器又是什麼呢?

任意對象, 只要定義了next(Python2) 或者next方法, 它就是一個迭代器. 就這麼簡單.

現在我們來理解迭代(iteration). 當我們使用一個循環來遍歷某個東西時, 這個過程本身就叫迭代.

二. 生成器(Generators)

1. 生成器基本概念

生成器也是一種迭代器, 但是你只能對其迭代一次. 這是因爲它們並沒有把所有的值存在內存中, 而是在運行時生成值. 你通過遍歷來使用它們, 要麼用一個“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

這個案例並不是非常實用. 生成器最佳應用場景是:你不想同一時間將所有計算出來的大量結果集分配到內存當中, 特別是結果集裏還包含循環. 這樣做會消耗大量資源. 許多Python 2裏的標準庫函數都會返回列表, 而Python 3都修改成了返回生成器, 因爲生成器佔用更少的資源.

下面是一個計算斐波那契數列的生成器:

# generator version
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)

用這種方式, 我們可以不用擔心它會使用大量資源. 然而, 之前如果我們這樣來實現的話:

def fibon(n):
    a = b = 1
    result = []
    for i in range(n):
        result.append(a)
        a, b = b, a + b
    return result

2. 理解生成器的”生成”

在測試前你需要再知道一個Python內置函數:next(). 它允許我們獲取一個序列的下一個元素. 那我們來驗證下我們的理解:

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

gen = generator_function()
print(next(gen))  # Output: 0
print(next(gen))  # Output: 1
print(next(gen))  # Output: 2
print(next(gen))  # Error

# Output: Traceback (most recent call last):
#            File "<stdin>", line 1, in <module>
#         StopIteration

我們可以看到, 在yield掉所有的值後, next()觸發了一個StopIteration的異常. 基本上這個異常告訴我們, 所有的值都已經被yield完了. 你也許會奇怪, 爲什麼我們在使用for循環時沒有這個異常呢?答案很簡單. for循環會自動捕捉到這個異常並停止調用next().

3. 內置數據類型中的可迭代對象

my_string = "Yasoob"
print(next(my_string))

# Output: Traceback (most recent call last):
#      File "<stdin>", line 1, in <module>
#    TypeError: str object is not an iterator

這個異常說那個str對象不是一個迭代器. 對, 就是這樣!它是一個可迭代對象, 而不是一個迭代器. 這意味着它支持迭代, 但我們不能直接對其進行迭代操作. 那我們怎樣才能對它實施迭代呢?是時候學習下另一個內置函數, iter. 它將根據一個可迭代對象返回一個迭代器對象. 這裏是我們如何使用它:

my_iter = iter("Yasoob")
print(next(my_iter))

# Output: 'Y'

參考文獻

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