1. 程式人生 > >第048講:魔法方法:迭代器

第048講:魔法方法:迭代器

目錄

0. 請寫下這一節課你學習到的內容:格式不限,回憶並複述是加強記憶的好方式!

測試題(筆試,不能上機哦~)

0. 請用你的話解釋一下“迭代”的概念?

1. 迭代器是一個容器嗎?

2. 迭代器可以回退(獲取上一個值)嗎?

3. 如何快速判斷一個容器是否具有迭代功能?

4. for 語句如何判斷迭代器裡邊已經取空了?

5. 在 Python 原生支援的資料結構中,你知道哪一個是隻能用迭代器訪問的嗎?

動動手(一定要自己動手試試哦~)

0. 用 while 語句實現與以下 for 語句相同的功能:

1. 寫一個迭代器,要求輸出至今為止的所有閏年。如:

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]