1. 程式人生 > 實用技巧 >python迭代器與生成器

python迭代器與生成器


title: python迭代器與生成器
data: 2018-4-7
categories:

  • python
    tags:
  • python

迭代器

迭代器(Iterator)是訪問集合元素的一種方式。迭代器物件從集合的第一個元素開始訪問,直到所有的元素被訪問完結束。迭代器只能往前不會後退。

迭代器有兩個基本的方法:iter()next()

python中對常見的迭代器使用場景是迴圈語句for,它用迭代器封裝集合,並且逐個訪問集合元素以執行迴圈體。比如:

for num in range(5):
    print(num)

其中的range()返回一個包含所指定元素的集合,而for語句將其封裝成一個迭代器後訪問。
使用iter()呼叫可以將列表、集合轉換成迭代器,比如下面的 t 就是一個迭代器:

>>> mylist = [1,3,4,8,6,9]
>>> t = iter(mylist)
>>> print(t)
<listiterator object at 0x00000000025F39B0>

迭代器和普通python物件的區別是迭代器有一個next()方法,每次呼叫該方法可以放回一個元素。呼叫者(比如for語句)可以通過不斷呼叫next()方法來逐個訪問集合元素。比如:

>>> iter = iter(range(5))
>>> print iter.next()
0
>>> print iter.next()
1
>>> print iter.next()
2
...

呼叫者可以一直這樣呼叫next()方法來訪問迭代器,直到next()返回StopIteration異常表示迭代已經完成。

建立一個迭代器

把一個類作為一個迭代器使用需要在類中實現兩個方法 __iter__() __next__()
Python 的建構函式為 __init__(), 它會在物件初始化的時候執行。

__iter__() 方法返回一個特殊的迭代器物件, 這個迭代器物件實現了 __next__() 方法並通過 StopIteration 異常標識迭代的完成。

__next__() 方法(Python 2 裡是 next())會返回下一個迭代器物件。

建立一個返回數字的迭代器,初始值為 1,逐步遞增 1:

#python3
class MyNumbers:
  def __iter__(self):
    self.a = 1
    return self
 
  def __next__(self):
    x = self.a
    self.a += 1
    return x
 
myclass = MyNumbers()
myiter = iter(myclass)
 
print(next(myiter))
print(next(myiter))
print(next(myiter))
print(next(myiter))
print(next(myiter))

執行輸出結果為:

1
2
3
4
5

StopIteration

StopIteration 異常用於標識迭代的完成,防止出現無限迴圈的情況,在 __next__() 方法中我們可以設定在完成指定迴圈次數後觸發 StopIteration 異常來結束迭代。
在 10 次迭代後停止執行:

#python3
class MyNumbers:
  def __iter__(self):
    self.a = 1
    return self
 
  def __next__(self):
    if self.a <= 10:
      x = self.a
      self.a += 1
      return x
    else:
      raise StopIteration
 
myclass = MyNumbers()
myiter = iter(myclass)
 
for x in myiter:
  print(x)

執行輸出結果為:

1
2
3
4
5
6
7
8
9
10

生成器

在 Python 中,使用了 yield 的函式被稱為生成器(generator)。

跟普通函式不同的是,生成器是一個返回迭代器的函式,只能用於迭代操作,更簡單點理解生成器就是一個迭代器。

在呼叫生成器執行的過程中,每次遇到 yield 時函式會暫停並儲存當前所有的執行資訊,返回 yield 的值, 並在下一次執行 next() 方法時從當前位置繼續執行。

呼叫一個生成器函式,返回的是一個迭代器物件。

以下例項使用 yield 實現斐波那契數列:

#!/usr/bin/python3
import sys
 
def fibonacci(n): # 生成器函式 - 斐波那契
    a, b, counter = 0, 1, 0
    while True:
        if (counter > n): 
            return
        yield a
        a, b = b, a + b
        counter += 1
f = fibonacci(10) # f 是一個迭代器,由生成器返回生成
 
while True:
    try:
        print (next(f), end=" ")
    except StopIteration:
        sys.exit()

執行以上程式,輸出結果如下:

0 1 1 2 3 5 8 13 21 34 55

使用 yield

#!/usr/bin/python3
import sys

def fibonacci(n,w=0): # 生成器函式 - 斐波那契
    a, b, counter = 0, 1, 0
    while True:
        if (counter > n): 
            return
        yield a
        a, b = b, a + b
        print('%d,%d' % (a,b))
        counter += 1
f = fibonacci(10,0) # f 是一個迭代器,由生成器返回生成

while True:
    try:
        print (next(f), end=" ")
    except :
        sys.exit()

輸出結果:

0 1,1
1 1,2
1 2,3
2 3,5
3 5,8
5 8,13
8 13,21
13 21,34
21 34,55
34 55,89
55 89,144

不使用 yield

#!/usr/bin/python3

import sys

def fibonacci(n,w=0): # 生成器函式 - 斐波那契
    a, b, counter = 0, 1, 0
    while True:
        if (counter > n): 
            return
        #yield a
        a, b = b, a + b
        print('%d,%d' % (a,b))
        counter += 1
f = fibonacci(10,0) # f 是一個迭代器,由生成器返回生成

while True:
    try:
        print (next(f), end=" ")
    except :
        sys.exit()

輸出結果:

1,1
1,2
2,3
3,5
5,8
8,13
13,21
21,34
34,55
55,89
89,144

區別

第二種沒有yield時,函式只是簡單執行,沒有返回迭代器f。這裡的迭代器可以用生成l列表來理解一下:

>>> l = [i for i in range(0,15)]
>>> print(l)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
>>> m = (i for i in range(0,15))
>>> print(m)
<generator object <genexpr> at 0x104b6f258>
>>> for g in m:
...     print(g,end=', ')
... 
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,

這裡的m就像上面的f一樣,是迭代器。

參考:http://www.runoob.com/python3/python3-iterator-generator.html 下面的筆記