Python 迭代器

Posted onetoinf

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python 迭代器相关的知识,希望对你有一定的参考价值。

迭代器就是重复地做一些事情,可以简单的理解为循环,在python中实现了__iter__方法的对象是可迭代的,实现了next()(在python3中使用__next__()代替了next方法)方法的对象是迭代器,这样说起来有点拗口,实际上要想让一个迭代器工作,至少要实现__iter__方法和next方法。很多时候使用迭代器完成的工作使用列表也可以完成,但是如果有很多值列表就会占用太多的内存,而且使用迭代器也让我们的程序更加通用、优雅、pythonic。

如果一个类想被用于for ... in循环,类似list或tuple那样,就必须实现一个__iter__()方法,该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的next()方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环。

不过迭代器是有限制的,例如

  • 不能向后移动
  • 不能回到开始
  • 也无法复制一个迭代器。
    因此要再次进行迭代只能重新生成一个新的迭代器对象。

获取迭代器

  1. 对于python内置的可迭代(iterable)对象,可以通过内置的iter()函数来获取相应的迭代器对象。
In [1]: a = [1,2,3,45]
 
In [2]: type(a)
Out[2]: list
 
In [3]: a = iter(a)
 
In [4]: type(a)
Out[4]: list_iterator

这样就获取了list相应的迭代器对象。

我们来看一下该迭代器对象的属性:

In [5]: dir(a)
Out[5]:
['__class__',
......
 '__iter__',
 '__le__',
......
 '__next__',
 '__reduce__',
......
 '__str__',
 '__subclasshook__']
 
In [6]:

可见此迭代对象具有两个特殊的成员方法__iter__()__next__(),这两个方法便是支持迭代器协议所需要实现的方法。其中__iter__()方法返回迭代器对象本身,__next__()方法返回容器的下一个元素,直到结尾抛出StopIteration异常。

我们来测试一下这个list_iterator对象的这两个方法:

__iter__()返回的对象就是迭代器对象本身。

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__()方法返回容器中的值直到结尾。

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]:
  1. 创建迭代器对象

除了使用iter()函数将内置的序列对象转换成相应的迭代器,我们可以自己实现迭代器协议创建迭代器对象,要实现迭代器协议也就是要在类中实现__iter__()__next__()方法。

下面写一个与list_iterator相同行为的迭代器:

# encoding:utf-8


class Fib(object):
    def __init__(self, n):
        self.gen = n
        self.a, self.b = 0, 1  # 初始化两个计数器a,b

    def __iter__(self):
        return self  # 实例本身就是迭代对象,故返回自己

    def __next__(self):
        self.a, self.b = self.b, self.a + self.b  # 计算下一个值
        if self.a > self.gen:  # 退出循环的条件
            raise StopIteration()
        return self.a  # 返回下一个值

if __name__ == '__main__':
    for i in Fib(3):
        print(i)

运行结果:

1
1
2
3
[Finished in 0.1s]

iter和next

  1. 内置函数iter()仅仅是调用了对象的__iter()__方法,所以list对象内部一定存在方法__iter__()
  2. 内置函数next()仅仅是调用了对象的__next()__方法,所以list对象内部不存在方法__next__(),但是Itrator中一定存在这个方法。
>>> L = [4, 5, 6]
>>> '__iter__' in dir(L)
True
>>> '__next__' in dir(L)
False
>>> I = L.__iter__()
>>> '__next__' in dir(I)
True
>>> from collections import Iterable, Iterator
>>> isinstance(L, Iterable)
True
>>> isinstance(L, Iterator)
False
>>> isinstance(I, Iterable)
True
>>> isinstance(I, Iterator)
True

Iterator继承自Iterable,从下面的测试中可以很方便的看到Iterator包含__iter()____next()__方法,而Iteratble仅仅包含__iter__()

>>> from collections import Iterator, Iterable
>>> help(Iterator)
Help on class Iterator:
 
class Iterator(Iterable)
 |  Method resolution order:
 |      Iterator
 |      Iterable
 |      builtins.object   
 |**注解:从这里可以看出Iterable继承自object, Iterator继承自Iterable。
 |  Methods defined here:
 |
 |  __iter__(self)
 |
 |  __next__(self)
 |      Return the next item from the iterator. When exhausted, raise StopIteration
......
>>> help(Iterable)
Help on class Iterable:
 
class Iterable(builtins.object)
 |  Methods defined here:
 |
 |  __iter__(self)
......

可迭代对象和迭代器对象

可迭代对象是实现了__iter__()方法的对象,__iter__()可以返回一个迭代器对象。
迭代器对象是实现了__next__()方法的对象,其中他的__iter__()返回的是迭代器对象本身。

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

上述例子中有着一个可迭代类和迭代器类

print('initialize a Iterable object......')
a = ListIterable([1, 2, 4, 5, 6])
print(type(a))
print('get Iterator object from __iter__ ')
b = a.__iter__()
print(type(b))

运行结果

initialize a Iterable object......
<class '__main__.ListIterable'>
get Iterator object from __iter__ 
call iterable __iter__().
<class '__main__.ListIterator'>
[Finished in 0.1s]

可见a是iterable对象(实现了__iter__()),b是iterator对象(实现了__next__())。

下面看看这样做是不是就可以重复多次迭代了:

a = ListIterable([1, 2, 4, 5, 6])

print([i for i in a])
print([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__().
[1, 2, 4, 5, 6]
call iterable __iter__().
call iterator __next__().
call iterator __next__().
call iterator __next__().
call iterator __next__().
call iterator __next__().
call iterator __next__().
[1, 2, 4, 5, 6]
[Finished in 0.1s]

重复迭代是可以了,从输出中我们可以看出一些什么来

  1. 我们在使用迭代工具对iterable对象进行迭代的时候首先调用的是iterable__iter__()方法,返回一个迭代器对象,也就是ListIterator的实例。
  2. 然后再遍历的时候是调用iterator的next方法输出值。
    这样就可以解释了为什么这样处理能够多次迭代了,因为每次使用迭代工具迭代的时候都会调用__iter__()返回一个新的迭代器对象,这样就相当于创建多个迭代器了,自然可以看起来是重复迭代了!

可变对象和迭代器

在迭代可变对象时候,一个序列的迭代器只是记录当前到达了序列中的第几个元素,所以如果在迭代过程中改变了序列的元素。更新会立即反应到所迭代的条目上。

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

这就是边迭代边更改元素带来的危险了!

复制迭代器

迭代器是一次性消耗品,使用完了以后就空了,请看。

>>> L=[1,2,3]
>>> I=iter(L)
>>> for i in I:
...     print(i, end='-')
...
1-2-3-
>>>next(I)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

>>> I=iter(L)
>>> J=I
>>> next(I)
1
>>> next(J)
2
>>> next(I)
3
>>> next(J)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

说明迭代器的复制只是浅复制而已。

以上是关于Python 迭代器的主要内容,如果未能解决你的问题,请参考以下文章

为啥我在这个 python 片段中得到 float 是不可迭代的? [复制]

python:可迭代对象,迭代器,生成器函数,生成器的解析举例代码说明

python:可迭代对象,迭代器,生成器函数,生成器的解析举例代码说明

python学习之-迭代器

python中迭代器(转)

Python概念之装饰器迭代器生成器