單迭代對象和多迭代對象

單迭代對象:只要對象定的話,無論你是否再調用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]



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章