看到相似__slots__這種形如__xxx__的變量或者函數名就要注意,這些在Python中是有特殊用途的。python
__iter__app
若是一個類想被用於for ... in循環,相似list或tuple那樣,就必須實現一個__iter__()方法,該方法返回一個迭代對象,而後,Python的for循環就會不斷調用該迭代對象的next()方法拿到循環的下一個值,直到遇到StopIteration錯誤時退出循環。函數
迭代器就是重複地作一些事情,能夠簡單的理解爲循環,在python中實現了__iter__方法的對象是可迭代的,實現了next()方法的對象是迭代器,這樣提及來有點拗口,實際上要想讓一個迭代器工做,至少要實現__iter__方法和next方法。不少時候使用迭代器完成的工做使用列表也能夠完成,可是若是有不少值列表就會佔用太多的內存,並且使用迭代器也讓咱們的程序更加通用、優雅、pythonic。spa
咱們以斐波那契數列爲例,寫一個Fib類,能夠做用於for循環:code
class Fib(object): def __init__(self): self.a, self.b = 0, 1 # 初始化兩個計數器a,b def __iter__(self): return self # 實例自己就是迭代對象,故返回本身 def next(self): self.a, self.b = self.b, self.a + self.b # 計算下一個值 if self.a > 100000: # 退出循環的條件 raise StopIteration(); return self.a # 返回下一個值
for n in Fib(): ... print n ... 1 1 2 3 5 ...
__getitem__對象
Fib實例雖然能做用於for循環,看起來和list有點像,可是,把它當成list來使用仍是不行,好比,取第5個元素:blog
Fib()[5] Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'Fib' object does not support indexing
要表現得像list那樣按照下標取出元素,須要實現__getitem__()方法:繼承
class Fib(object): def __getitem__(self, n): a, b = 1, 1 for x in range(n): a, b = b, a + b return a >>> f = Fib() >>> f[0] 1 >>> f[1] 1 >>> f[2] 2 >>> f[3]
可是list有個神奇的切片方法:接口
>>> range(100)[5:10] [5, 6, 7, 8, 9] #對於Fib卻報錯。緣由是__getitem__()傳入的參數多是一個int,也多是一個切片對象slice,因此要作判斷: class Fib(object): def __getitem__(self, n): if isinstance(n, int): a, b = 1, 1 for x in range(n): a, b = b, a + b return a if isinstance(n, slice): start = n.start stop = n.stop a, b = 1, 1 L = [] for x in range(stop): if x >= start: L.append(a) a, b = b, a + b return L #如今試試Fib的切片: >>> f = Fib() >>> f[0:5] [1, 1, 2, 3, 5] >>> f[:10] [1, 1, 2, 3, 5, 8, 13, 21, 34, 55] #可是沒有對step參數做處理: >>> f[:10:2] [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
也沒有對負數做處理,因此,要正確實現一個__getitem__()仍是有不少工做要作的。內存
此外,若是把對象當作dict,__getitem__()的參數也多是一個能夠做key的object,例如str。
與之對應的是__setitem__()方法,把對象視做list或dict來對集合賦值。最後,還有一個__delitem__()方法,用於刪除某個元素。
總之,經過上面的方法,咱們本身定義的類表現得和Python自帶的list、tuple、dict沒什麼區別,這徹底歸功於動態語言的「鴨子類型」,不須要強制繼承某個接口。