前言
前篇:
魔術方法(1):使用__str__與__repr__進行字符串操作
正文
魔術方法(2):使用__iter__與__next__實現可迭代
for循環可能是我們最熟悉的循環操作,但是你是否思考過,for循環內部做了些什麼?它怎麼知道什麼時候停止循環?又是如何保證元素的遍歷不會出錯?
突然講原理可能難以理解,先仔細觀察下列代碼和運行結果,認真分析一下打印的先後情況
一個基於魔術方法實現的平方迭代器
運行結果
在開始講解之前,我們先補充幾個知識點:
專業術語的定義
可迭代對象:實現了__iter__方法,也就是可以使用for循環的對象
迭代器:如果一個可迭代對象裡面也實現了__next__方法,那麼這就是一個迭代器,也就是可以使用next()函數的對象
生成器:一種特殊的迭代器,自動實現了__iter__和__next__方法,也就是調用包含yield的函數所返回的對象(這種用法後面會在高級語法中講解)
順帶一提,Python的三大基本序列類型就是list,tuple和range,怎麼樣,沒想到吧,range的位格這麼高,這可是官方文檔(點擊查看)自己說的
--------分割--------
接下來讓我們逐步分析上述代碼:
當我們對一個對象使用for i in XXX的時候,在這一步就會調用該對象的__iter__方法。因為返回的對象會交給for循環處理,所以通常我們會返回的其實就是對象本身即return self
在接下來每次讀取值的時候,就會調用對象的__next__方法來得到下一個值,當返回的是停止信號(即StopIteration異常)時,讀取結束,這也是為什麼會多打印出一個__next__
也就是說使用__iter__來返回一個東西交給for循環,而__next__來逐步獲得下一個值,直到拋出StopIteration
請務必理解上述執行過程,然後讓我們用於實踐
--------分割--------
假如有一個班級類,裡面保存了很多學生的姓名,基本代碼如下
一個簡單的教室類,用來保存學生姓名
如果我們希望遍歷這些學生信息,你也許會使用
for student in classroom.students:
但是我個人來說並不喜歡這樣,這並不優雅,而且暴露變量的引用往往意味著危險,外部的改變隨時會改變對象內部的狀態:
my_student = classroom.students
my_student.pop()
print(classroom.students)
當我將對象classroom的學生列表引用暴露出去,再使用這個引用彈出一個元素,對象內部的學生列表也發生了變化,這正是需要警惕的地方(誰說python沒指針.doge)
現在我們來給Classroom類實現一個迭代屬性,讓我們可以對教室進行for循環!
可迭代的教室類
運行結果
這裡有一個取巧的方法,不同於上面所說的“__iter__通常返回對象本身”,這裡我們完全可以將遍歷委託給students列表來實現,我們也不需要關心__next__的返回內容和跳出判斷。