说明
《Python 教程》 持续更新中,提供建议、纠错、催更等加作者微信: gairuo123(备注:pandas教程)和关注公众号「盖若」ID: gairuo。跟作者学习,请进入 Python学习课程。欢迎关注作者出版的书籍:《深入浅出Pandas》 和 《Python之光》。
Python 对于容器类型数据支持逐个进行迭代处理,迭代会对所有元素按照一个逻辑进行计算操作。因此在 Python 所有数据范围内就存在类型是否是可迭代的话题。为了高效完成迭代操作,Python 专门设计了迭代器类型,这类数据专门用来迭代操作。最后,为了高效快捷创建一个迭代器类型,Python 又有一个生成器类型成生成一个可迭代对象。
迭代器(iterator)就是一个封装了迭代的对象。Python中内置的序列,如list、tuple、str、bytes、dict、set、collections.deque 等都是可迭代对象,但它们不是迭代器。迭代器可以被 next() 函数调用,并不断返回下一个值。Python 从可迭代的对象中获取迭代器。
迭代器和生成器都是为了惰性求值(lazy evaluation),避免浪费内存空间,实现高效处理大量数据。在 Python 3中,生成器有广泛的用途,所有生成器都是迭代器,因为生成器完全实现了迭代器接口。迭代器用于从集合中取出元素,而生成器用于"凭空"生成元素 。PEP 342 给生成器增加了 send() 方法,实现了"基于生成器的协程"。PEP 380允许生成器中可以return返回值,并新增了 yield from 语法结构,打开了调用方和子生成器的双向通道。
迭代器用于支持:
*
拆包实参判断对象是否为迭代器:
from collections import abc
isinstance([1,2,3], abc.Iterator)
# False
isinstance((1,2,3), abc.Iterator)
# False
isinstance({'name': 'lily', 'age': 18}, abc.Iterator)
# False
isinstance({1, 2, 3}, abc.Iterator)
# False
isinstance('abc', abc.Iterator)
# False
isinstance(123, abc.Iterator)
# False
# 生成器表达式,见下文
isinstance((x*2 for x in range(5)), abc.Iterator)
# True
可以看到,常见的类型,它们不是迭代器类型,我们来看看如何让一个对象成为迭代器类型。要转为迭代器类型用使用 内置函数 iter(),要使用 内置函数 iter() 需要对此对象支持此函数。
对于容器类型,需要实现 container.__iter__()
方法来提供可扩展的支持,用来支持使用内置函数 iter() 将其转为 迭代器类型,Python 大多内置的容器已经实现了这个方法。
from collections import abc
it = iter('abc')
it
# <str_iterator at 0x7f8482595e70>
isinstance(it, abc.Iterator)
# True
对于其他类型对象,从技术上讲,Python 迭代器对象必须支持迭代器协议。谓迭代器协议,就是要求一个迭代器必须要实现如下两个方法:
iterator.__iter__()
:返回迭代器对象本身iterator.__next__()
:从容器中返回下一个项也就是说,一个对象只要支持上面两个方法,就是迭代器。
我们来自己实现一个迭代器对象:
class PowTwo:
"""Class to implement an iterator
of powers of two 二的幂"""
def __init__(self, max=0):
self.max = max
def __iter__(self):
self.n = 0
return self
def __next__(self):
if self.n <= self.max:
result = 2 ** self.n
self.n += 1
return result
else:
raise StopIteration
我们来验证和执行:
PowTwo(3)
# <__main__.PowTwo at 0x7f84823bdf60>
[*PowTwo(3)]
# [1, 2, 4, 8]
n = PowTwo(3)
it = iter(n)
from collections import abc
isinstance(it, abc.Iterator)
# True
next(it) # 1
next(it) # 2
next(it) # 4
next(it) # 8
next(it) # StopIteration
Python 定义了几种迭代器对象以支持对一般和特定序列类型、字典和其他更特别的形式进行迭代。 除了迭代器协议的实现,特定类型的其他性质对迭代操作来说都不重要。
一旦迭代器的 __next__()
方法引发了 StopIteration,它必须一直对后续调用引发同样的异常。 不遵循此行为特性的实现将无法正常使用。
此外,还可以实现一个无限的迭代器,即元素是无限的,永远迭代不完。以下我们实现是一个无限偶数迭代器:
class Even:
"""Infinite iterator to return all
Even numbers"""
def __iter__(self):
self.num = 2
return self
def __next__(self):
num = self.num
self.num += 2
return num
执行时永远迭代不完:
# 注意!这个看就行,别执行,会死循环
[*Even()]
e = iter(Even())
next(e) # 2
next(e) # 4
next(e) # 6
next(e) # 8
next(e) # 10
# ...
内容函数 iter() 也可以生成无限迭代器,在传入两个参数的形式中,调用 callable 直到它返回 sentinel(哨兵,这里设置一个它不可能到达的值)。如:
int()
# 0
inf = iter(int,1)
next(inf) # 0
next(inf) # 0
在处理此类迭代器时,我们必须小心,注意要设置终止条件,否则非常容易造成死循环。
使用迭代器的优点是可以节省资源,比如上例中,我们可以在不将整个数字系统存储在内存中的情况下获得所有偶数。我们可以在有限的内存中拥有无限的内容项(理论上)。
总结下,迭代器的特点:
Python 内置库 itertools 中的大多数函数是返回各种迭代器对象,如果自己去实现同样的功能,代码量会非常大,而在运行效率上反而更低,因此,我们很有必要了解一下这个标准库。
更新时间:2022-04-05 17:00:21 标签:python 迭代器 迭代