單迭代對象:只要對象定的話,無論你是否再調用iter()對象,甚至是把這個對象賦值給其他變量名,你會發現這些變量指的是同一個地方
多迭代對象:你可以通過iter()方法來產生一個迭代器對象,並且將它賦值給一個變量名,如果你再對對象調用iter()方法的話,把它賦值給另一個變量名,你會發現這兩個迭代對象不是指向同一個位置。
你可以把每一個iterator object想象成一個容器,它裏面儲存了對它指向的對象處理的狀態信息,譬如next()在什麼地方了。所以對只要迭代對象沒有變,那麼它們使用的就是同一個狀態信息。這也是之所以有單迭代對象和多迭代對象的原因--關鍵在狀態信息
所以說對形式[i for i in A]它只會對A求一次iter()方法,因爲你如果像下面進行while時,每個while循環都對A重新求一次iter()調用,相當於前面迭代的信息完全使用不了了。這個肯定是不行的。
字符串,元組,列表都是多迭代對象
>>> a=(1,2,3)
>>> a1=iter(a) #定義一個指向對象a的迭代對象
>>> a1
<tuple_iterator object at 0xb714ab0c> #說明a1是一個指向元組的迭代對象,有了它以後你就可以對該元組迭代了
>>> a2=iter(a)
>>> a2
<tuple_iterator object at 0xb714ac0c> #與迭代對象a1比較,它們是不同的位置
>>> next(a1)
1
>>> next(a2) #對比顯示他們處理同一個對象,但是各自都保存着狀態信息
1
map是一個單迭代對象
>>> m=(4,5,6)
>>> m1=map(abs,m)
>>> m1
<map object at 0xb714abcc> #生成一個map 對象
>>> m2=iter(m1) #對同一個對象調用iter方法,發現他們實際上指的是同一個迭代對象
>>> m2 #變量名只是用來指向對象的,迭代對象都一樣了,狀態信息肯定也一樣
<map object at 0xb714abcc>
>>> m3=iter(m1)
>>> m3
<map object at 0xb714abcc>
>>> next(m2) #因爲iterator object一樣,所以next的實際上是同一個對象
4
>>> next(m3)
5
我們可以通過map函數來嵌套一組可迭代對象
>>> F=map(iter,((1,2,3),(5,4),(6,7))) #map嵌套了一些元組的iterator object
>>> F
<map object at 0xb710588c>
>>> F1=next(F) #對map調用next方法,它得到的是指向第一個元組的iterator object
>>> F1
<tuple_iterator object at 0xb71058cc>
>>> next(F1) #既然F1是一個iterator object我們就可以對它進行next操作
1 #得到下一層的值
>>> F2=next(F)
>>> F2
<tuple_iterator object at 0xb710584c>
>>> next(F2)
5
對比下面的形式
>>> for i in ((1,2,3),(4,5)): #我們把內嵌的元組當成一個整體,不能對他們進行迭代訪問
... print(i)
...
(1, 2, 3)
(4, 5)
>>> a=((1,2,3),(5,6))
>>> a1=iter(a)
>>> next(a1) #你無法再進一步進行訪問了
(1, 2, 3)
range對象是一個可迭代對象,同時是一個多迭代對象
>>> next(a)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: range object is not an iterator
>>> a1=iter(a) #對同一個對象進行iter調用
>>> a2=iter(a)
>>> a1 #下面迭代對象進行對比,發現不是同一個對象,所以狀態信息就是各顧各的
<range_iterator object at 0xb71388c0>
>>> a2
<range_iterator object at 0xb7138d10>
>>> next(a1) #因爲iterator object不一樣,所以上一個迭代對象的狀態信息不會體現在當前迭代對象
0
>>> next(a2)
0
易錯點:
下面一段代碼是python手冊改變過來的,用於定義myzip 函數
>>> def myzip(*args):
... iters = map(iter,args)
... while iters:
... res=[next(i) for i in iters]
... yield tuple(res)
...
上面這段代碼運行正常的前提是map返回的是值,而不是單次可迭代對象,在python3中上面的式子將陷入死循環。
正確的做法使用list來創建一個支持多次迭代的對象
1:for 循環中next(iters)報StopIterator信息,只會是停止這個for循環
2:while 循環中for 中的iters會在每一輪的循環開始重新進行iter()函數調用,然後執行next方法。
3:只有當next(i)報錯的時候,while循環纔會結束
>>> def myzip(*args):
... iters=list(map(iter,args))
... while iters:
... res=[next(i) for i in iters]
... yield tuple(res)
...
>>> list(myzip('abc','lms'))
[('a', 'l'), ('b', 'm'), ('c', 's')]
>>> iters=list(map(iter,('abc','lms')))
>>> iters #現在是個元組,多迭代對象
[<str_iterator object at 0xb714ae8c>, <str_iterator object at 0xb714ac2c>] #關鍵點,雖然說iters對象的迭代對象每一次while循環都會變,但是指向'abc'和'lms'的迭代對象沒變,所以狀態信息一直保存着
列表解析中的for循環它會讀取所有的元組,然後因爲指向這些string的迭代對象沒變,所以next(i)一直與前面的關聯着,直到某一項中的next(i)報錯。這個時候while循環結束
現在分析一下爲啥上面的形式是一個死循環:
因爲你在for 循環中已經對next(iters)到出現StopIterator了,也就是到尾了,這個時候我們可以發現它還是能夠輸出('a','I'),但是你第二輪while循環的時候,對map對象調用iter(),它仍舊是上一輪的對象,但是它裏面的邏輯已經到尾了,所以next(iters)會是stopIterator,然後這個循環就結束了。res=[],就這樣循環着等於[]
>>> iters=map(iter,('abc','lms'))
>>> iters
<map object at 0xb7111acc>
>>> while iters:
... res=[next(i) for i in iters]
... print(res)
... break
...
['a', 'l'] #說明當傳進去一個map單迭代對象時它也是處理了,但都是第一個。
>>>count=0
>>>a=(1,2)
>>> while iters: #說明while循環中for循環中的對象每一輪都會重新生成一個新的迭代對象
... res=[i for i in a]
... print(res)
... count+=1
... if count>2:
... break
...
[1, 2]
[1, 2]
[1, 2]