Python 迭代器與生成器 - PyTips 0x01

PyTips

項目地址:https://git.io/pytips

迭代器與生成器

迭代器(iterator)與生成器(generator)是 Python 中比較常用又很容易混淆的兩個概念,今天就把它們梳理一遍,并舉一些常用的例子。

for 語句與可迭代對象(iterable object):

for i in [1, 2, 3]:
    print(i)
1
2
3
obj = {"a": 123, "b": 456}
for k in obj:
    print(k)
b
a

這些可以用在 for 語句進行循環的對象就是可迭代對象。除了內置的數據類型(列表、元組、字符串、字典等)可以通過 for 語句進行迭代,我們也可以自己創建一個容器,包含一系列元素,可以通過 for 語句依次循環取出每一個元素,這種容器就是迭代器(iterator)。除了用 for 遍歷,迭代器還可以通過 next() 方法逐一讀取下一個元素。要創建一個迭代器有3種方法,其中前兩種分別是:

  1. 為容器對象添加 __iter__()__next__() 方法(Python 2.7 中是 next());__iter__() 返回迭代器對象本身 self__next__() 則返回每次調用 next() 或迭代時的元素;
  2. 內置函數 iter() 將可迭代對象轉化為迭代器
# iter(IterableObject)
ita = iter([1, 2, 3])
print(type(ita))

print(next(ita))
print(next(ita))
print(next(ita))

# Create iterator Object
class Container:
    def __init__(self, start = 0, end = 0):
        self.start = start
        self.end = end
    def __iter__(self):
        print("[LOG] I made this iterator!")
        return self
    def __next__(self):
        print("[LOG] Calling __next__ method!")
        if self.start < self.end:
            i = self.start
            self.start += 1
            return i
        else:
            raise StopIteration()
c = Container(0, 5)
for i in c:
    print(i)
        
<class 'list_iterator'>
1
2
3
[LOG] I made this iterator!
[LOG] Calling __next__ method!
0
[LOG] Calling __next__ method!
1
[LOG] Calling __next__ method!
2
[LOG] Calling __next__ method!
3
[LOG] Calling __next__ method!
4
[LOG] Calling __next__ method!

創建迭代器對象的好處是當序列長度很大時,可以減少內存消耗,因為每次只需要記錄一個值即刻(經??吹饺藗兘榻B Python 2.7 的 range 函數時,建議當長度太大時用 xrange 更快,在 Python 3.5 中已經去除了 xrange 只有一個類似迭代器一樣的 range)。

生成器

前面說到創建迭代器有3種方法,其中第三種就是生成器(generator)。生成器通過 yield 語句快速生成迭代器,省略了復雜的 __iter__() & __next__() 方式:

def container(start, end):
    while start < end:
        yield start
        start += 1
c = container(0, 5)
print(type(c))
print(next(c))
next(c)
for i in c:
    print(i)
<class 'generator'>
0
2
3
4

簡單來說,yield 語句可以讓普通函數變成一個生成器,并且相應的 __next__() 方法返回的是 yield 后面的值。一種更直觀的解釋是:程序執行到 yield 會返回值并暫停,再次調用 next() 時會從上次暫停的地方繼續開始執行:

def gen():
    yield 5
    yield "Hello"
    yield "World"
    yield 4
for i in gen():
    print(i)
5
Hello
World
4

Python 3.5 (準確地說應該是 3.3 以后)中為生成器添加了更多特性,包括 yield from 以及在暫停的地方傳值回生成器的 send()等,為了保持簡潔這里就不深入介紹了,有興趣可以閱讀官方文檔說明以及參考鏈接2。

參考

  1. Iterators & Generators
  2. How the heck does async/await work in Python 3.5?
  3. Python's yield from
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容