第048講:魔法方法:迭代器
目錄
0. 請寫下這一節課你學習到的內容:格式不限,回憶並複述是加強記憶的好方式!
5. 在 Python 原生支援的資料結構中,你知道哪一個是隻能用迭代器訪問的嗎?
0. 用 while 語句實現與以下 for 語句相同的功能:
2. 要求自己寫一個 MyRev 類,功能與 reversed() 相同(內建函式 reversed(seq) 是返回一個迭代器,是序列 seq 的逆序顯示)。
0. 請寫下這一節課你學習到的內容:格式不限,回憶並複述是加強記憶的好方式!
自始至終,有一個概念,我們一直都在使用,但是我們從來沒有認真地去剖析它,這個概念就是 迭代。
迭代的意思就類似於迴圈,每一次重複的過程被稱之為一次迭代的過程,而每一次迭代得到的結果將會被用來作為下一次迭代的初始值,那麼提供迭代方法的容器我們稱之為迭代器。
通常我們接觸的迭代器有序列(序列就是列表、元組、字串)和字典,它們都支援迭代操作,舉例說明:
我們通常使用 for 語句進行迭代,
>>> for i in "來自江南的你": print(i) 來 自 江 南 的 你
字串就是一個容器,同時也是一個迭代器,for 語句的作用就是觸發這個迭代器的迭代功能,每次從容器裡面依次拿出一個數據,這就是所謂的迭代操作。同時,字典和檔案也是支援迭代操作的,例如:
>>> dict1 = {1 : 'one', 2 : 'two', 3 : 'three', 4 : 'four'}
>>> for each in dict1:
print("%s -> %s"%(each, dict1[each]))
1 -> one
2 -> two
3 -> three
4 -> four
關於迭代操作,Python提供了兩個 BIF:iter() 和 next()
對於一個容器物件,呼叫 iter() 就得到它的迭代器,呼叫 next() ,迭代器就會返回下一個值,那麼怎麼結束呢,如果沒有值可以返回了,Python 就會丟擲一個 StopIteration 的異常。舉例:
>>> string = '來自江南的你'
>>> it = iter(string)
>>> next(it)
'來'
>>> next(it)
'自'
>>> next(it)
'江'
>>> next(it)
'南'
>>> next(it)
'的'
>>> next(it)
'你'
>>> next(it)
Traceback (most recent call last):
File "<pyshell#47>", line 1, in <module>
next(it)
StopIteration
我們知道 for 語句是自動呼叫迭代器的,那麼通過這兩個 BIF 我們大概就能分析出 for 語句的工作原理:
>>> while True:
try:
each = next(it)
except StopIteration:
break
print(each)
來
自
江
南
的
你
大家看到了,我們用 while 語句同樣可以還原 for 語句,
>>> for each in string:
print(each)
來
自
江
南
的
你
那麼,我們現在講的是魔法方法,關於迭代器,它的魔法方法一共是兩個:__iter__() 和 __next__()
- iter()
__iter__()
- next()
__next__()
沒錯,它們對應的就是剛才的兩個BIF的實現,一個容器如果是迭代器,那麼就應該實現 __iter__() 這個魔法方法,這個方法其實是返回迭代器本身,就是 return(self),接下來重點要實現的是 __next__() 這個魔法方法,因為它跌定了迭代器的規則,舉個簡單的例子:
#斐波那契數列
>>> class Fibs:
def __init__(self):
self.a = 0
self.b = 1
def __iter__(self):
return self
def __next__(self):
self.a, self.b = self.b, self.a + self.b
return self.a
>>> fibs = Fibs()
>>> for each in fibs:
print(each)
1
1
2
3
5
...
這個會沒完沒了,不能停下來,但是基本功能實現了,下面改進,加一個限制條件(20以內的數列):
>>> for each in fibs:
if each < 20:
print(each)
else:
break
1
1
2
3
5
8
13
我們發現了,我們剛剛實現的這個迭代器的惟一的亮點就是沒有終點,我們可以加入一個引數來控制迭代的範圍:
>>> class Fibs:
def __init__(self, n = 10):
self.a = 0
self.b = 1
self.n = n
def __iter__(self):
return self
def __next__(self):
self.a, self.b = self.b, self.a + self.b
if self.a > self.n:
raise StopIteration
return self.a
這樣就不會沒完沒了了:
>>> fibs = Fibs()
>>> for each in fibs:
print(each)
1
1
2
3
5
8
而且,你可以控制它的數量,這裡改為100:
>>> fibs = Fibs(100)
>>> for each in fibs:
print(each, end = ' ')
1 1 2 3 5 8 13 21 34 55 89
測試題(筆試,不能上機哦~)
0. 請用你的話解釋一下“迭代”的概念?
答:迭代是重複反饋過程的活動,其目的通常是為了接近併到達所需的目標或結果。每一次對過程的重複被稱為一次“迭代”,而每一次迭代得到的結果會被用來作為下一次迭代的初始值。
1. 迭代器是一個容器嗎?
答:不是。因為我們耳熟能詳的容器像列表,字典,元組都是可以存放資料的,而迭代器就是實現了__next__()方法的物件(用於遍歷容器中的資料)。
2. 迭代器可以回退(獲取上一個值)嗎?
答:迭代器性質決定沒有辦法回退,只能往前進行迭代。但這並不是什麼很大的缺點,因為我們幾乎不需要在迭代途中進行回退操作。
3. 如何快速判斷一個容器是否具有迭代功能?
答:判斷該容器是否擁有 __iter__() 和 __next__() 魔法方法。
4. for 語句如何判斷迭代器裡邊已經取空了?
答:迭代器通過 __next__() 方法每次返回一個元素,並指向下一個元素。如果當前位置已無元素,通過丟擲 StopIteration 異常表示。
5. 在 Python 原生支援的資料結構中,你知道哪一個是隻能用迭代器訪問的嗎?
答:set。對於原生支援隨機訪問的資料結構(如tuple、list),可以使用迭代器或者下標索引的形式訪問,但對於無法隨機訪問的資料結構 set 而言,迭代器是唯一的訪問元素的方式。
動動手(一定要自己動手試試哦~)
0. 用 while 語句實現與以下 for 語句相同的功能:
for each in range(5):
print(each)
程式碼清單:
alist = range(5)
it = iter(alist)
while True:
try:
print(next(it))
except StopIteration:
break
1. 寫一個迭代器,要求輸出至今為止的所有閏年。如:
>>> leapYears = LeapYear()
>>> for i in leapYears:
if i >=2000:
print(i)
else:
break
2012
2008
2004
2000
提示:閏年判定法((year%4 == 0 and year%100 != 0) or (year%400 == 0))
程式碼清單:
import datetime as dt
class LeapYear:
def __init__(self):
self.now = dt.date.today().year
def isLeapYear(self, year):
if (year%4 == 0 and year%100 != 0) or (year%400 == 0):
return True
else:
return False
def __iter__(self):
return self
def __next__(self):
while not self.isLeapYear(self.now):
self.now -= 1
temp = self.now
self.now -= 1
return temp
2. 要求自己寫一個 MyRev 類,功能與 reversed() 相同(內建函式 reversed(seq) 是返回一個迭代器,是序列 seq 的逆序顯示)。
例如:
>>> myRev = MyRev("FishC")
>>> for i in myRev:
print(i, end='')
ChsiF
程式碼清單:
class MyRev:
def __init__(self, data):
self.data = data
self.index = len(data)
def __iter__(self):
return self
def __next__(self):
if self.index == 0:
raise StopIteration
self.index = self.index - 1
return self.data[self.index]