天天看點

進階特性:疊代器

文章大部分參考來源:No-Coder's Blog

1. 可疊代對象

我們已經知道可以對list、tuple、str等類型的資料使用for...in...的循環文法從其中依次拿到資料進行使用,我們把這樣的過程稱為周遊,也叫疊代。

但是,是否所有的資料類型都可以放到for...in...的語句中,然後讓for...in...每次從中取出一條資料供我們使用,即供我們疊代嗎?

#!/usr/bin/env python2.7
# -*- coding=utf-8 -*-


for i in 100:
    print i

# >>> TypeError: 'int' object is not iterable
# int類型的對象是不可疊代的


class MyList(object):
    def __init__(self):
        self.names = []
    
    def add(self, value):
        self.names.append(value)


mylist = MyList()
mylist.add('1')
mylist.add('2')
mylist.add('3')


for i in mylist:
    print i

# >>> TypeError: 'MyList' object is not iterable
# MyList類型的對象是不可疊代的

           

2. 如何判斷一個對象是否可疊代

"""
可以使用isinstance來判斷對象是否為可疊代的
"""

from collections import Iterable

isinstance([], Iterable)
# True

isinstance({}, Iterable)
# True

isinstance('abc', Iterable)
# True

isinstance(mylist, Iterable)
# False

isinstance(100, Iterable)
# False
           

3. 可疊代對象的本質

“”“
讓我們來看下能夠被for...in循環取值的類型到底都有什麼相同的特性?
使用dir發現list tuple dict 這些資料類型都有一個函數叫__iter__函數是不是實作了這個函數 我們對應建立的内容就是可疊代的
”“”

class MyList(object):
    def __init__(self):
        self.names = []
    
    def add(self, value):
        self.names.append(value)

    def __iter__(self):
        pass


mylist = MyList()
mylist.add('1')
mylist.add('2')
mylist.add('3')


for i in mylist:
    print i

# >>> TypeError: iter() returned non-iterator of type 'NoneType'
# MyList類型的對象iter函數傳回的不是關于iterator的對象
# 那我們再去修改一下


class MyList(object):
    def __init__(self):
        self.names = []
    
    def add(self, value):
        self.names.append(value)

    def __iter__(self):
        return MyIterator(self)


class MyIterator(object):
        def __init__(self, mylist):
                self.mylist = mylist
                self.current = 0 

        def __next__(self):
                if self.current < len(self.mylist.container):
                        item = self.mylist.container[self.current]
                        self.current += 1
                        return item
                else:
                        raise StopIteration

mylist = MyList()
mylist.add(1)
mylist.add(2)   
mylist.add(3)
for i in mylist:
    print(i)

# 1 2 3 
# 成功我們先不管MyIterator是什麼,我們目前關心的是隻要對應的對象中實作了iter并且傳回了對應的疊代器對象這個時候我們實作的對象就可以稱之為可疊代的

           

4. iter()以及next()

li_iter = iter(li)
next(li_iter)
# 11
next(li_iter)
# 22
next(li_iter)
# 33
next(li_iter)
# 44
next(li_iter)
# 55
next(li_iter)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

"""
list、tuple等都是可疊代對象,我們可以通過iter()函數擷取這些可疊代對象的疊代器。然後我們可以對擷取到的疊代器不斷使用next()函數來擷取下一條資料。iter()函數實際上就是調用了可疊代對象的__iter__方法。而__iter__方法則傳回包含next函數的疊代器對象,然後我們使用疊代器對象的next方法不停的取出下一個值,直到raise為止
"""
           

5. 如何判斷一個對象是否為疊代器

from collections import Iterator

isinstance([], Iterator)
# False

isinstance(iter([]), Iterator)
# True

isinstance(iter("abc"), Iterator)
# True
           

6. 疊代器iterator

"""
通過上面的分析,我們已經知道,疊代器是用來幫助我們記錄每次疊代通路到的位置,當我們對疊代器使用next()函數的時候,疊代器會向我們傳回它所記錄位置的下一個位置的資料。實際上,在使用next()函數的時候,調用的就是疊代器對象的__next__方法(Python3中是對象的__next__方法,Python2中是對象的next()方法)。是以,我們要想構造一個疊代器,就要實作它的__next__方法。但這還不夠,python要求疊代器本身也是可疊代的,是以我們還要為疊代器實作__iter__方法,而__iter__方法要傳回一個疊代器,疊代器自身正是一個疊代器,是以疊代器的__iter__方法傳回自身即可。
總結來說:一個實作了__iter__方法和__next__方法的對象,就是疊代器。
"""

class MyList(object):

        def __init__(self):
                self.container = []

        def add(self, item):
                self.container.append(item)

        def __iter__(self):
                """傳回一個疊代器"""
                return MyIterator(self)


class MyIterator(object):
    
        def __init__(self, mylist):
                self.mylist = mylist
                self.current = 0 

        def __next__(self):
                if self.current < len(self.mylist.container):
                        item = self.mylist.container[self.current]
                        self.current += 1
                        return item
                else:
                        raise StopIteration

if __name__ == "__main__":
        mylist = MyList()
        mylist.add(1)
        mylist.add(2)   
        mylist.add(3)

        for i in mylist:
            print(i)
           

7. for.....in的本質

for item in items  循環的本質就是先通過iter()函數擷取可疊代對象Iterable的疊代器Iterator,然後對擷取到的疊代器不斷調用next()方法來擷取下一個值并将其指派給item,當遇到StopIteration的異常後循環結束。

8. 疊代器簡單的練習題

# 斐波那契實作

class Feb(object):
    def __init__(self,num):
        self.num = num
        self.index = 0
        self.a = 0
        self.b = 1

    def __iter__(self):
        return self

    def next(self):
        if self.index < self.num:
            v = self.a
            self.a, self.b = self.a + self.b, self.a
            self.index += 1
            return v

        else:
            raise StopIteration


if __name__ == '__main__':
    f = Feb(20)
    for i in f:
        print i
           
# 倒叙取值

class Reverse(object):


    def __init__(self,list):
        self.num = len(list)
        self.target = list

    def __iter__(self):
        return self

    def next(self):
        self.num -= 1
        if self.num >= 0:
            return self.target[self.num]
        else:
            raise StopIteration

if __name__ == '__main__':
    list  = [1, 2, 3, 4, 5, 6, 7, 8]
    r = Reverse(list)
    for i in r:
        print i
           

學習到這我們總結一下:

可疊代對象(實作了iter方法,iter方法傳回疊代器對象)

疊代器對象(實作了iter以及next方法)