zoukankan      html  css  js  c++  java
  • python 迭代器(转)

    迭代器
    
    迭代器是在python2.2中被加入的,它为类序列对象提供了一个类序列的接口。有了迭代器可以迭代一个不是序列的对象,因为他表现出了序列的行为。当在python中使用for循环迭代一个对象时,调用者几乎分辨不出他迭代的是一个迭代器对象还是一个序列对象,因为python让他(迭代器)像一个序列那样操作。
    
    如何迭代
    
    本质上说迭代器是个对象,但是这个对象有个特殊的方法next()(在python3中使用__next__()代替了next方法)。当使用for循环来遍历整个对象时候,就会自动调用此对象的__next__()方法并获取下一个item。当所有的item全部取出后就会抛出一个StopIteration异常,这并不是错误的发生,而是告诉外部调用者迭代完成了,外部的调用者尝试去捕获这个异常去做进一步的处理。
    
    不过迭代器是有限制的,例如
    
    不能向后移动
    不能回到开始
    也无法复制一个迭代器。
    因此要再次进行迭代只能重新生成一个新的迭代器对象。
    获取迭代器
    
    对于python内置的可迭代(iterable)对象,可以通过内置的iter()函数来获取相应的迭代器对象。
    Python
    
     a = [1,2,3,45]
     type(a)
     list
     a = iter(a)
     type(a)
     list_iterator
    1
    2
    3
    4
    5
    6
    7
    8
    9
    a = [1,2,3,45] type(a) list
    a = iter(a) type(a) list_iterator 这样就获取了list相应的迭代器对象。 我们来看一下该迭代器对象的属性: Python dir(a) ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__length_hint__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__'] 可见此迭代对象具有两个特殊的成员方法__iter__()和__next__(),这两个方法便是支持迭代器协议所需要实现的方法。其中__iter__()方法返回迭代器对象本身,__next__()方法返回容器的下一个元素,直到结尾抛出StopIteration异常。 我们来测试一下这个list_iterator对象的这两个方法: __iter__()返回的对象就是迭代器对象本身。 Python a = [1,2,3,45] a = iter(a) a.__iter__() <list_iterator at 0x3a33f10>a <list_iterator at 0x3a33f10>a is a.__iter__() True In [1]: a = [1,2,3,45] In [2]: a = iter(a) In [3]: a.__iter__() Out[3]: <list_iterator at 0x3a33f10> In [4]: a Out[4]: <list_iterator at 0x3a33f10> In [5]: a is a.__iter__() Out[5]: True In [6]: __next__()方法返回容器中的值直到结尾。 Python In [6]: a.__next__() Out[6]: 1 In [7]: a.__next__() Out[7]: 2 In [8]: a.__next__() Out[8]: 3 In [9]: a.__next__() Out[9]: 45 In [10]: a.__next__() ------------------------------------------ StopIteration Traceback (most recent call last) <ipython-input-10-73aa2c76d676> in <module>() ----> 1 a.__next__() In [6]: a.__next__() Out[6]: 1 In [7]: a.__next__() Out[7]: 2 In [8]: a.__next__() Out[8]: 3 In [9]: a.__next__() Out[9]: 45 In [10]: a.__next__() ------------------------------------------ StopIteration Traceback (most recent call last) <ipython-input-10-73aa2c76d676> in <module>() ----> 1 a.__next__() StopIteration: In [11]: 2. 创建迭代器对象 除了使用iter()函数将内置的序列对象转换成相应的迭代器,我们可以自己实现迭代器协议创建迭代器对象,要实现迭代器协议也就是要在类中实现__iter__()和__next__()方法。 下面我写一个与list_iterator相同行为的迭代器: Python class ListIter(object): def __init__(self, data): self.__data = data self.__count = 0 def __iter__(self): return self def __next__(self): if self.__count < len(self.__data): val = self.__data[self.__count] self.__count += 1 return val else: raise StopIteration class ListIter(object): def __init__(self, data): self.__data = data self.__count = 0 def __iter__(self): return self def __next__(self): if self.__count < len(self.__data): val = self.__data[self.__count] self.__count += 1 return val else: raise StopIteration 我们就可以使用for循环来遍历这个迭代器了: Python In [16]: a = ListIter([1,2,3,4,5]) In [17]: for i in a: ....: print(i) ....: In [16]: a = ListIter([1,2,3,4,5]) In [17]: for i in a: ....: print(i) ....: 对于迭代器对象,使用for循环遍历整个数组其实是个语法糖,他的内部实现还是通过调用对象的__next__()方法。 实际上他内部的工作原理应该是这样的: a = ListIter([1, 2, 3, 4, 5]) while True: try: i = a.__next__() except StopIteration: break // do something in for loop print(i) a = ListIter([1, 2, 3, 4, 5]) while True: try: i = a.__next__() except StopIteration: break // do something in for loop print(i) 迭代器支持多次迭代 正如前面所说的迭代器对象不支持重新迭代,也就是同一个迭代器对象无法多次迭代,如: Python In [19]: a = ListIter([1,2,3,4,5]) In [20]: [i for i in a] Out[20]: [1, 2, 3, 4, 5] In [21]: [i for i in a] Out[21]: [] In [19]: a = ListIter([1,2,3,4,5]) In [20]: [i for i in a] Out[20]: [1, 2, 3, 4, 5] In [21]: [i for i in a] Out[21]: [] In [22]: 可见,当我再次迭代迭代器a的时候便只返回了空列表,这是因为for循环直接捕获了StopIteration异常。如果要再次迭代生成列表的话只能重新生成一个新的迭代器对象。 为了能够解决这个问题,可以分别定义一个可迭代对象(iterables)和迭代器对象(iterator). 插入小插曲: 对于可迭代对象和迭代器对象,我的理解是: 可迭代对象是实现了__iter__()方法的对象,__iter__()可以返回一个迭代器对象。 迭代器对象是实现了__next__()方法的对象,其中他的__iter__()返回的是迭代器对象本身。 我把代码做了修改,如下: Python class ListIterable(object): def __init__(self, data): self.__data = data def __iter__(self): print("call iterable __iter__().") return ListIterator(self.__data) class ListIterator(object): def __init__(self, data): self.__data = data self.__count = 0 def __iter__(self): print("call iterator __iter__().") return self def __next__(self): print("call iterator __next__().") if self.__count < len(self.__data): val = self.__data[self.__count] self.__count += 1 return val else: raise StopIteration class ListIterable(object): def __init__(self, data): self.__data = data def __iter__(self): print("call iterable __iter__().") return ListIterator(self.__data) class ListIterator(object): def __init__(self, data): self.__data = data self.__count = 0 def __iter__(self): print("call iterator __iter__().") return self def __next__(self): print("call iterator __next__().") if self.__count < len(self.__data): val = self.__data[self.__count] self.__count += 1 return val else: raise StopIteration 为了知道python何时调用__iter__()方法,我添加了一个printf函数来做标记。 现在把这两个类导入到当前空间中: Python In [1]: from list_iter import * In [2]: a = ListIterable([1,2,4,5,6]) In [3]: b = a.__iter__() call iterables __iter__(). In [4]: a Out[4]: <list_iter.ListIterable at 0x39446d0> In [5]: b Out[5]: <list_iter.ListIterator at 0x39447b0> In [1]: from list_iter import * In [2]: a = ListIterable([1,2,4,5,6]) In [3]: b = a.__iter__() call iterables __iter__(). In [4]: a Out[4]: <list_iter.ListIterable at 0x39446d0> In [5]: b Out[5]: <list_iter.ListIterator at 0x39447b0> In [6]: 可见a是iterable对象(实现了__iter__()),b是iterator对象(实现了__next__())。 下面看看这样做是不是就可以重复多次迭代了: Python In [6]: [i for i in a] call iterable __iter__(). call iterator __next__(). call iterator __next__(). call iterator __next__(). call iterator __next__(). call iterator __next__(). call iterator __next__(). Out[6]: [1, 2, 4, 5, 6] In [7]: [i for i in a] call iterable __iter__(). call iterator __next__(). call iterator __next__(). call iterator __next__(). call iterator __next__(). call iterator __next__(). call iterator __next__(). Out[7]: [1, 2, 4, 5, 6] In [6]: [i for i in a] call iterable __iter__(). call iterator __next__(). call iterator __next__(). call iterator __next__(). call iterator __next__(). call iterator __next__(). call iterator __next__(). Out[6]: [1, 2, 4, 5, 6] In [7]: [i for i in a] call iterable __iter__(). call iterator __next__(). call iterator __next__(). call iterator __next__(). call iterator __next__(). call iterator __next__(). call iterator __next__(). Out[7]: [1, 2, 4, 5, 6] In [8]: 重复迭代是可以了,从输出中我们可以看出一些什么来 我们在使用迭代工具对iterable对象进行迭代的时候首先调用的是iterable的__iter__()方法,返回一个迭代器对象,也就是ListIterator的实例。 然后再遍历的时候是调用iterator的next方法输出值。 这样就可以解释了为什么这样处理能够多次迭代了,因为每次使用迭代工具迭代的时候都会调用__iter__()返回一个新的迭代器对象,这样就相当于创建多个迭代器了,自然可以看起来是重复迭代了! 可变对象和迭代器 在迭代可变对象时候,一个序列的迭代器只是记录当前到达了序列中的第几个元素,所以如果在迭代过程中改变了序列的元素。更新会立即反应到所迭代的条目上。 我写了个测试看了下,的确: Python In [13]: c = [1,2,3,4,5] In [14]: d = iter(c) In [15]: for i in c: ....: print(i) ....: c.remove(i) ....: In [13]: c = [1,2,3,4,5] In [14]: d = iter(c) In [15]: for i in c: ....: print(i) ....: c.remove(i) ....: 可见上面边迭代边删除列表的元素,但是最后却只输出了1, 3, 5,这是为啥? 既然迭代器只记得是在列表中第几个元素,那么当在第0个元素的时候将会输出1然后删除1,这是列表变成了 Python [2, 3, 4, 5] 1 [2, 3, 4, 5] 但是迭代器记得我是在第二个位置上面,就指向了列表中的第二个位置上,也就是3,然后输出3. 以此类推,最后只能输出1,3,5了。 如果我猜测的没错的话,剩余的列表应该只剩下2和4了: Python In [17]: c Out[17]: [2, 4] 1 2 In [17]: c Out[17]: [2, 4]
  • 相关阅读:
    Git基本操作二
    Git基本操作一
    Mysql查询一
    接口的token验证
    Laravel模型的一些小技巧
    AOP编程思想实现全局异常处理
    5.4 RegExp类型
    5.4.1 RegExp实例属性
    5.4.2 RegExp实例方法
    5.4.3 RegExp构造函数属性
  • 原文地址:https://www.cnblogs.com/wsw-seu/p/8006120.html
Copyright © 2011-2022 走看看