Python 迭代器 & __iter__方法

看到相似__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沒什麼區別,這徹底歸功於動態語言的「鴨子類型」,不須要強制繼承某個接口。

相關文章
相關標籤/搜索