python的迭代器iterator



       可以被next函数调用不断返回下一个值的对象称为迭代器(iterator),可以直接用于for循环的对象称为可迭代对象(iterable),所有的可迭代对象均可以通过iter函数转变为迭代器。事实上,for循环内部先调用iter()把iterable变成iterator然后再进行循环迭代

         iterator可以用来访问集合元素,迭代器对象从集合的首元素开始访问,直到所有元素被访问完结束,只能向前访问。迭代器的一个优点是事先不需要准备好整个迭代过程中的所有元素,仅仅在迭代到某个元素时才计算该元素,在这之前或之后,元素可以不存在或被销毁。通过next()函数,可以取到每次yield,访问到最后的时候,会抛出stopiteration异常。但是,如果用户提供了default参数之后,当取出所有元素,再次调用next函数会返回default,不会抛出Stopiteration异常

>>> l=[1,2,3,4]

>>> a=iter(l)
>>> next(a)
1

>>> next(a)
2
>>> next(a)
3
>>> next(a)
4
>>> next(a)

Traceback (most recent call last):
  File "<pyshell#9>", line 1, in <module>
    next(a)
StopIteration

>>> from collections import Iterator, Iterable

>>> isinstance(a, Iterable)
True
>>> isinstance(a, Iterator)
True
>>> isinstance(l, Iterable)
True
>>> isinstance(l, Iterator)
False

对于文件对象来说,next函数也同样适用,next函数的作用就和readline类似,都是一次读取文件的一行内容。但是readline读取到末尾的时候返回的是空字符串,next函数则会引发StopIteration异常

比如:

>>> f=open(r'E:\sublimetext2\config.php')

>>> f.readline()
'<?php\n'
>>> next(f)
'\n'

使用for循环显示读取文件内容:

for  line  in open("config.php").readlines():

    print  line

for循环实现了迭代器,迭代器每次只从对象读取一条数据,不会造成过大的内存开销。但是,它实际上是把文件一次性加载到内存中再逐行打印出来。如果文件很庞大,内存的开销就大了.

更快的方法:

for  line   in  open("config.php"):

      print  line

这里没有显示读取文件,而是通过迭代器读取每一行内容

或者这样写:

with  open(r'E:\sublimetext2\config.php')  as   f:

   for  line  in  f:

        print  line

前面提到for循环实际上就是对可迭代对象转换为迭代器,然后通过调用next函数来实现遍历,我们可以将二者做过等价替换:

for  a in [1,2,3]:

    print  a

<=====>

b=iter([1,2,3])

try:

  while  1:

       print  next(b)

except  StopIteration:

      pass

因此我们也可以总结出:

凡是可作用于for循环的对象都是iterable类型;

凡是可作用于next()函数或者__next__()方法的都是iterator类型

自定义迭代器的大概过程是这样的:

def  iter(obj):

      return  obj.__iter__()

#python2:

def   next(obj):

     return  obj.next()

#python3

def   next(obj):

     return  obj.__next__()

可迭代对象内部一定是包含了__iter__()的魔法方法,list对象内部就一定存在__iter__()方法,__iter__()就是返回iterator实例的

next()函数只是调用了对象的__next__()(或者next())方法,list对象内部一定不存在__next__()方法,迭代器一定存在__next__()

所以,当我们要将iterable转换为iterator的时候,可以使用iterator=iterable.__iter__(),比如a=l.__iter__()

当我们使用“iterable.__next__()”(比如l.__next__())的时候会报错,当使用”iterator.__next__()” (a.__next__(),python2用a.next())的时候不报错。

注意:python3中可以使用__next__()来获取下一个值,但是在python2中要用.next()

迭代器(iterator)必须至少要定义 __iter__() 和 __next__() 两个方法

class Iterator:
    def __init__(self):
        self.start=-1
   
    def __next__(self):
        res=self.start
        self.start +=3
        if res >10:
            raise StopIteration
        return res
    def __iter__(self):
        return self

>>> a=Iterator()
>>> print(list(a))
[-1, 2, 5, 8]

上面是python3版本,下面是python2版本

>>> class Iterator:
    def __init__(self):
        self.start=-1
   
    def next(self):
        res=self.start
        self.start +=3
        if res >10:
            raise StopIteration
        return res
    def __iter__(self):
        return self

   
>>> a=Iterator()
>>> print list(a)
[-1, 2, 5, 8]

通过上面你可以注意到__iter__的返回值都是self,事实上,当用户自定义迭代器的时候__iter__返回的就是对象自身

自定义迭代器的时候,通过在类里面定义一个__iter__和__next__方法,用第一个来返回第二个的返回对象

只要是实现了__iter__()方法的对象,就可以使用迭代器进行访问

 

也行会问,既然for循环实际上就是用了迭代器的原理,那么迭代器还有什么不可替代或者提供比较好的处理方案的情况呢?

1.对于那些支持随机访问的数据结构,比如list、tuple还有dict,迭代器对比for并没有优势,而且还缺失了索引值(尽管可以使用内置函数enumerate找回索引,但是也增加了繁琐度)。但是,对于无法随机访问的数据结构set(),迭代器是唯一的访问元素方式。

比如:

>>> s={'a','b','c'}

>>> a=s.__iter__()

>>> a.next()
'a'
>>> a.next()
'c'
>>> a.next()
'b'

2.节省内存空间,前面提到事先不需要准备好整个迭代过程中的所有元素,对于数据庞大的结构和文件,迭代器是一种理想的访问方式。如果我们是要对xml文件进行处理的时候,由于etree.iterparse序列化的xml tree是可迭代对象,我们可以渐进式的处理内容

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值