python中的迭代器詳解


#原創,轉載請先聯系

理論性的東西有點枯燥,耐心點看~

1.迭代是什么?

我們知道可以對list,tuple,dict,str等數據類型使用for...in的循環語法,從其中依次取出數據,這個過程叫做遍歷,也叫迭代。迭代是訪問集合元素的一種常用的方式。

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

2.可迭代對象是什么?

簡單來說,可以用for...in循環語句,從其中依次取出數據的對象,就是可迭代對象。例如,列表、元組、字典、字符串都是可迭代對象。整數、浮點數、布爾值都是不可迭代的。

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

3.迭代器是什么?

在可迭代對象進行迭代的時候,即用for...in...循環語法依次取出數據時,過程是怎樣的呢?

我們發現,每迭代一次(即在for...in...中每循環一次)都會返回對象中的下一條數據,一直向后讀取數據直到迭代了所有數據后結束。那么,在這個過程中就應該有一個“人”去記錄每次訪問到了第幾條數據,以便每次迭代都可以返回下一條數據。我們把這個能幫助我們進行數據迭代的“人”稱為迭代器(Iterator)。舉個栗子,老師安排一個班的同學每節課上課前進行演講,按學號順序進行,A同學這節課演講,老師就會記住這節課是A同學演講,那么下節課就是B同學進行演講...依次類推,在這個例子里,老師就是一個迭代器。

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

4.怎樣獲取可迭代對象的迭代器?

我們可以通過iter()內置函數取得可迭代對象的迭代器。

list = [1,2,3,4,5]  # list是可迭代對象
lterator = iter(list)  # 通過iter()方法取得list的迭代器
print(lterator)

輸出:
<list_iterator object at 0x7f35e6537a20>

迭代器是獲取到了,那么應該怎樣用呢?

next()函數是通過迭代器獲取下一個位置的值。

注意: 當我們已經迭代完最后一個數據之后,再次調用next()函數會拋出StopIteration的異常,來告訴我們所有數據都已迭代完成,不用再執行next()函數了。

list = [1,2,3,4,5]  # list是可迭代對象
lterator = iter(list)  # 通過iter()方法取得list的迭代器
print(next(lterator))  # 1
print(next(lterator))  # 2
print(next(lterator))  # 3
print(next(lterator))  # 4
print(next(lterator))  # 5
print(next(lterator))

輸出:
1
2
3
4
5
Traceback (most recent call last):
  File "/home/chichung/桌面/課堂練習/協程/iter方法取得可迭代對象的迭代器.py", line 8, in <module>
    print(next(lterator))
StopIteration

看完覺得奇怪的同學,是不是覺得一個對象能拿出一個迭代器很amazing。好像編的一樣,別急,繼續看上去,下面有答案。

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

5.判斷一個對象是否可迭代

isinstance(object,classinfo)內置函數可以判斷一個對象是否是一個已知的類型,類似 type()。

  • object -- 實例對象。
  • classinfo -- 可以是直接或間接類名、基本類型或者由它們組成的元組。

在這之前,還需要知道collections模塊里的Iterable。通俗點講,凡是可迭代對象都是這個類的實例對象。下面來驗證一下:

import collections

print(isinstance([1, 2, 3], collections.Iterable))
print(isinstance((1,2,3), collections.Iterable))
print(isinstance({"name":"chichung","age":23}, collections.Iterable))
print(isinstance("sex", collections.Iterable))
print(isinstance(123,collections.Iterable))
print(isinstance(True,collections.Iterable))
print(isinstance(1.23,collections.Iterable))

輸出:
True
True
True
True
False
False
False

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

6.可迭代對象怎么可以獲取迭代器呢?

看完第4點,有些人就感覺很奇怪,例如:[1,2,3]不就是一個列表,一個對象嗎?怎么還能拿出一個迭代器來了?

首先,我們從第5點可以知道,可迭代對象其實都是collections模塊里的Iterable類創建出來的實例的。你寫一個列表,以為他不是任何類創建的,只是單純一個列表?不是的,其實它就是Iterable類創建的實例對象。點進Iterable的類看一下,你會發現新大陸。

class Iterable(metaclass=ABCMeta):

    __slots__ = ()

    @abstractmethod
    def __iter__(self):  # 注意點 while False:
            yield None

原來由Iterable創建的對象,是有一個魔方方法__iter__(self)的。這個方法就是返回一個迭代器的。所以,由Iterable類創建的實例對象,是可以拿出一個迭代器的。

接下來要說的有點繞......

之所以Iterable類創建的對象是可迭代對象,是因為Iterable類有這個方法!不信?我就來編寫一個能創建可迭代對象的類。

 

import collections

class BecomeIterable:

    def __iter__(self):
        """返回一個空的迭代器"""
        return None

people = BecomeIterable()
print(isinstance(people, collections.Iterable))

輸出:
True

 

騷不騷?什么都沒有,就一個魔法方法,創建的對象就是可迭代對象了。

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

7.迭代器為什么能用next()函數進行迭代?

我們知道,可以用iter()函數,在可迭代對象中獲取迭代器。例如:iterator = iter([1,2,3])

這樣一看,迭代器也就是一個對象而已啊,為什么他可以用next()函數,一下子出來一個值,一下子又出來一個值。

其實是這樣的,iter()函數能調用可迭代對象的魔方方法__iter__(),從而返回一個迭代器。怎么返回的呢?__iter__()方法是使用collections模塊里的Iterator類來創建一個迭代器對象。

接下來看下Iterator的一部分源代碼:

class Iterator(Iterable):

    __slots__ = ()

    @abstractmethod
    def __next__(self):
        'Return the next item from the iterator. When exhausted, raise StopIteration'
        raise StopIteration

    def __iter__(self):
        return self

是不是茅塞頓開了?和第6點的解釋原理一樣。這里就不詳細解釋了。稍微注意下魔方方法__next__()的最后一句,如果超出迭代方位就拋出StopIteration異常。

還有一點需要注意,迭代器的源代碼也有__iter__()魔方方法,所以,Iterator也是一個可迭代對象呀!!!

所以,如果你喜歡在迭代器里面再取出迭代器也是可以的,但是好像有點無聊......目前還不知道有什么應用到......

 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

8.創建一個迭代器類

理論講完了額...不懂的還是要多看幾遍。下面開始應用~

如果我們編寫一段代碼,想把結果一個一個迭代出來,這時候就需要編寫迭代器類了。

class MyIterator():
    def __init__(self):
        self.list = []
        self.position = 0

    def add_name(self,name):
        self.list.append(name)

    def __iter__(self):
        return self  # 返回一個迭代器

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


people = MyIterator()  # people對象既是一個迭代器,也是一個可迭代對象
people.add_name("張三")
people.add_name("李四")
people.add_name("王五")

# 把people當做一個迭代器來看時
print(next(people))
print(next(people))
print(next(people))

# 把humen當做一個可迭代對象來看時
humen = MyIterator()  # 因為迭代器只能用一次,再用會拋出錯誤,所以需要再創建
humen.add_name("張三")
humen.add_name("李四")
humen.add_name("王五")
iterator = iter(humen)  # iter()方法獲取可迭代對象的迭代器
print(next(iterator))
print(next(iterator))
print(next(iterator))

輸出:
張三
李四
王五
張三
李四
王五

注意看注釋,如果原理都懂,這其實是一個很簡單的例子。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM