单迭代对象:只要对象定的话,无论你是否再调用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]