一 什么是迭代?什么是迭代器?

迭代是重复反馈过程的活动,其目的通常是为了逼近所需目标或者结果。每一次对过程的重复就是一次迭代,而每一次迭代得到的结果会作为下一次迭代的初始值
大白话就是重复执行同一个操作或者活动,下一次迭代的开始是上一次迭代的结束。
迭代器是一个对象,它的工作是遍历和选择序列中的对象,它提供了一种访问容器对象中各个元素而又不必暴露容器内部细节的方法
迭代器可以实现迭代操作。

二 python中的迭代器

要想了解迭代器就得知道迭代器协议,就像要想知道Web就得知道HTTP协议一样。协议是一种规则,只有满足了这些规则,你才能使用该协议衍生出来的东西。

1. python中的迭代器协议

python中的迭代器协议是指:一个对象需要提供next方法,该方法要么返回迭代对象中的下一项,要么就抛StopIteration异常,迭代终止。

2. 迭代器的实现原理

在python中,只要一个对象实现了以下两个方法,那么它就是一个迭代器
_ iter _: 返回一个可迭代对象
_ next _: 返回迭代器的下一项,当没有元素可以返回时抛StopIteration异常,迭代终止。

下面用上面的两个方法来实现一个返回斐契那波数列的迭代器,如下

from collections.abc import Iterable, Iterator

#实现一个返回斐契那波数列的迭代器
class fibo_iterator():

    def __init__(self):
        self.a = 0
        self.b = 1

    def __iter__(self):
        # 由于自己就是一个迭代器,所以返回自己就行
        return self

    def __next__(self):

        if self.b > 100:
            raise StopIteration
        else:
            self.a, self.b = self.b, self.a + self.b
            return self.a
            

我们可以用collections.abc库和内置函数 isinstance() 来判断一个·对象是不是一个指定类型的对象。现在我们用它们来判断fibo_iterator是不是一个迭代器,如下

fibo = fibo_iterator()
print(type(fibo))
print("是否是可迭代对象:", isinstance(fibo, Iterable))
print("是否是迭代器:", isinstance(fibo, Iterator))
print(dir(fibo))
>>>
<class '__main__.fibo_iterator'>
是否是可迭代对象: True
是否是迭代器: True
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'a', 'b']

从结果可以看出,我们上面实现的 fibo 确实是一个迭代器,因为实现了__next__ 和__iter__ 方法。其中__iter__ 返回一个可迭代对象,如上结果所示。接下来看看 _ next _ 方法。

fibo = fibo_iterator()
print("第一次:", next(fibo))
print("第二次:", next(fibo))
print("第三次:", next(fibo))
print("第四次:", next(fibo))
print("第五次:", next(fibo))
>>>
第一次: 1
第二次: 1
第三次: 2
第四次: 3
第五次: 5

上面用内置函数next() 对迭代器fibo进行 迭代操作,其实是 next() 调用了迭代器里面的
看 _ next _ 方法,从而实现了迭代操作。


三 python中的可迭代对象

python中规定:凡是实现了__iter__ (或者__getitem__)方法的都是可迭代对象(Iterable),可迭代对象都可以被迭代器(Iterator)进行迭代操作。

1.可迭代对象

python中基础序列基本都是可迭代对象,我们可以用内置函数 isinstance() 来进行判断,如下

from collections.abc import Iterable, Iterator

#python的可迭代对象
ls = [1, 2, 3, 4, 5]
tu = ('a', 'b')
dic = {'a': 1, 'b': 2}
strs = 'abcd'
se = {'d', 2, 3}

print("列表是不是可迭代对象:", isinstance(ls, Iterable))
print("元组是不是可迭代对象:", isinstance(tu, Iterable))
print("字典是不是可迭代对象:", isinstance(dic, Iterable))
print("字符串是不是可迭代对象:", isinstance(strs, Iterable))
print("集合是不是可迭代对象:", isinstance(se, Iterable))
print('分割线'.center(50, '='))
print(ls.__dir__())
print(tu.__dir__())
print(dic.__dir__())
print(strs.__dir__())
print(se.__dir__())

>>>
列表是不是可迭代对象: True
元组是不是可迭代对象: True
字典是不是可迭代对象: True
字符串是不是可迭代对象: True
集合是不是可迭代对象: True
=======================分割线========================
['__repr__', '__hash__', '__getattribute__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__iter__', '__init__', '__len__', '__getitem__', '__setitem__', '__delitem__', '__add__', '__mul__', '__rmul__', '__contains__', '__iadd__', '__imul__', '__new__', '__reversed__', '__sizeof__', 'clear', 'copy', 'append', 'insert', 'extend', 'pop', 'remove', 'index', 'count', 'reverse', 'sort', '__doc__', '__str__', '__setattr__', '__delattr__', '__reduce_ex__', '__reduce__', '__subclasshook__', '__init_subclass__', '__format__', '__dir__', '__class__']
['__repr__', '__hash__', '__getattribute__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__iter__', '__len__', '__getitem__', '__add__', '__mul__', '__rmul__', '__contains__', '__new__', '__getnewargs__', 'index', 'count', '__doc__', '__str__', '__setattr__', '__delattr__', '__init__', '__reduce_ex__', '__reduce__', '__subclasshook__', '__init_subclass__', '__format__', '__sizeof__', '__dir__', '__class__']
['__repr__', '__hash__', '__getattribute__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__iter__', '__init__', '__len__', '__getitem__', '__setitem__', '__delitem__', '__contains__', '__new__', '__sizeof__', 'get', 'setdefault', 'pop', 'popitem', 'keys', 'items', 'values', 'update', 'fromkeys', 'clear', 'copy', '__doc__', '__str__', '__setattr__', '__delattr__', '__reduce_ex__', '__reduce__', '__subclasshook__', '__init_subclass__', '__format__', '__dir__', '__class__']
['__repr__', '__hash__', '__str__', '__getattribute__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__iter__', '__mod__', '__rmod__', '__len__', '__getitem__', '__add__', '__mul__', '__rmul__', '__contains__', '__new__', 'encode', 'replace', 'split', 'rsplit', 'join', 'capitalize', 'casefold', 'title', 'center', 'count', 'expandtabs', 'find', 'partition', 'index', 'ljust', 'lower', 'lstrip', 'rfind', 'rindex', 'rjust', 'rstrip', 'rpartition', 'splitlines', 'strip', 'swapcase', 'translate', 'upper', 'startswith', 'endswith', 'isascii', 'islower', 'isupper', 'istitle', 'isspace', 'isdecimal', 'isdigit', 'isnumeric', 'isalpha', 'isalnum', 'isidentifier', 'isprintable', 'zfill', 'format', 'format_map', '__format__', 'maketrans', '__sizeof__', '__getnewargs__', '__doc__', '__setattr__', '__delattr__', '__init__', '__reduce_ex__', '__reduce__', '__subclasshook__', '__init_subclass__', '__dir__', '__class__']
['__repr__', '__hash__', '__getattribute__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__iter__', '__init__', '__sub__', '__rsub__', '__and__', '__rand__', '__xor__', '__rxor__', '__or__', '__ror__', '__isub__', '__iand__', '__ixor__', '__ior__', '__len__', '__contains__', '__new__', 'add', 'clear', 'copy', 'discard', 'difference', 'difference_update', 'intersection', 'intersection_update', 'isdisjoint', 'issubset', 'issuperset', 'pop', '__reduce__', 'remove', '__sizeof__', 'symmetric_difference', 'symmetric_difference_update', 'union', 'update', '__doc__', '__str__', '__setattr__', '__delattr__', '__reduce_ex__', '__subclasshook__', '__init_subclass__', '__format__', '__dir__', '__class__']

从上面结果可知,列表,元组,字典,字符串和集合都是可迭代对象,因为它们都实现了__iter__(或者__getitem__)方法

2.可迭代对象如何被迭代?

操作层面我们对可迭代对象进行迭代取值很简单,用关键字for,如下

ls = [1, 2, 3, 4, 5]

for i in ls:
    print(i)
>>>
1
2
3
4
5

那么它(可迭代对象)是如何被迭代的呢?这就和上面的迭代器联系起来了,如下
可迭代对象如何被迭代:

  • 当对可迭代对象进行迭代(for)操作时,首先会调用内置函数iter()返回一个迭代器(Iterator)
  • 对返回的迭代器调用 next()函数,逐个读取迭代器内容
ls = [1, 2, 3, 4, 5]
#for对可迭代对象进行迭代
for i in ls:
    print(i)
    
print('分割线'.center(40, '*'))
#for原理解析
ls_iterator = iter(ls)
print(type(ls_iterator))

print(next(ls_iterator))
print(next(ls_iterator))
print(next(ls_iterator))
print(next(ls_iterator))
print(next(ls_iterator))

1
2
3
4
5
******************分割线*******************
<class 'list_iterator'>
1
2
3
4
5

可以看到,用for和用iter(),next() 对可迭代对象进行迭代取值效果是一样,因为for本来就是对iter()和next()的封装。

综上,用for对可迭代对象进行迭代的操作如下:

在这里插入图片描述
以上流程就体现了在对可迭代对象遍历取值时迭代器(Iterator)充当“工具”的作用。

3.可迭代对象和迭代器的区别

从上面来看可迭代对象与迭代器的区别就很明显了,如下:

  • 在实现上的不同可迭代对象只要实现__iter__方法就行,而迭代器要实现__iter__和__next__方法,最大区别就是__next__方法。
  • 作用上的不同可迭代对象一般是一些序列,文件等,而迭代器则是提供了一种对可迭代对象进行迭代取值的工具。

4.思考:迭代器为什么要是可迭代的(Iterable)?

思考:迭代器为什么是可迭代对象(Iterable)?提示:next()



以上就是本人对python迭代器的一些思考和拙见,如有误,还望指出。如有帮助,点个赞。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐