单迭代对象和多迭代对象

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



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值