迭代器和生成器是 Python 中用于实现迭代协议的两个重要概念。它们都用于遍历序列(如列表、元组、字符串等)或其他可迭代对象。
# 迭代器
# 基础概念
在 Python 中,可迭代对象( Iterable
)是指可以逐个返回元素的对象,比如列表、元组、字典、集合等。它们都实现了 __iter__()
方法。
迭代器( Iterator
)是实现了两个方法的对象:
__iter__()
:返回迭代器对象本身,通常该方法不做任何操作,直接返回 self。__next__()
:返回集合中的下一个元素。如果没有更多元素,抛出 StopIteration 异常。
迭代器的核心在于 __next__()
方法,它让我们可以一遍一遍地遍历可迭代对象。
** 迭代器的特性 **
迭代器是一个可以记住遍历位置的对象。
迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不能后退。
迭代器是 Python 中的容器类的数据类型,可以同时存储多个数据,取迭代器中的数据只能一个一个地取,而且取出来的数据在迭代器中就不存在了。
# 内置迭代器
Python 中的许多内置对象是可迭代的,包括:
- 列表:[1, 2, 3]
- 元组:(1, 2, 3)
- 字符串:"hello"
- 字典:{'a': 1, 'b': 2}(可以迭代字典的键、值或键值对)
- 集合:
我们可以通过 iter ()
函数将这些可迭代对象转化为迭代器,然后使用 next ()
获取元素。
list=[1,2,3,4] | |
it = iter (list) # 将列表转化为迭代器对象 | |
print (next (it)) # 使用 next () 访问元素 | |
>>> 1 | |
print (next (it)) | |
>>> 2 |
如果没有更多元素,调用 next ()
会抛出 StopIteration
异常:
# 遍历直到 StopIteration 异常 | |
try: | |
while True: | |
print (next (iterator)) | |
except StopIteration: | |
print ("迭代结束") | |
sys.exit () |
** 使用 for 循环 **
实际上,Python 中的 for 循环会自动处理迭代器。我们可以直接用 for 循环遍历任何可迭代对象,无需显式使用 next ():
list=[1,2,3,4] | |
it = iter (list) # 转化为迭代器对象 | |
for x in it: | |
print (x) |
# 自定义迭代器
迭代器是一个实现了迭代协议的对象。迭代协议意味着迭代器必须拥有 __iter__
和 __next__
方法。当一个对象实现了这两个方法,我们就称它为迭代器。
__iter__
方法返回迭代器自身,而 __next__
方法返回序列中的下一个值。
如果序列已经到了末尾,则抛出 StopIteration
异常。
创建一个返回数字的迭代器,初始值为 0,逐步递增 1,迭代次数 limit:
import sys | |
class MyIterator (object): | |
# 1. 初始化属性 | |
def __init__(self, limit): | |
self.limit = limit # 表示迭代次数 | |
self.current = 0 # 表示当前的元素. | |
# 2. 返回迭代器对象本身. | |
def __iter__(self): | |
return self # self = 迭代器对象. | |
# 3. 自定义代码,表示:迭代器的规则,即:获取下一个元素. | |
def __next__(self): | |
# 3.1 判断,数据不合法,就停止迭代. | |
if self.current >= self.limit: | |
# raise: 抛出异常,并终止程序运行的. | |
raise StopIteration # 抛出异常,StopIteration: 迭代结束. | |
# raise StopIteration (' 达到了迭代限制。') | |
# 3.2 获取下个元素,并返回即可. | |
self.current += 1 | |
return self.current | |
if __name__ == '__main__': | |
# 1. 创建迭代器对象. | |
my_iter = MyIterator (5) # 底层调用 iter 魔法方法,返回本身 (迭代器对象本身) | |
print (type (my_iter)) # <class '__main__.MyIterator'> | |
# 2. 从迭代器中获取元素. | |
print (f'get item {next (my_iter)}') # 底层调用 next 魔法方法,返回下一个元素 | |
# 遍历. | |
for i in my_iter: # 底层调用 next 魔法方法,返回下一个元素 | |
print (f'for item {i}') | |
# 遍历完后,迭代器已经没有元素了,继续迭代会报错. | |
try: | |
print (next (my_iter)) # 底层调用 next 魔法方法,返回下一个元素 | |
except StopIteration as e: | |
print (e) | |
sys.exit () |
StopIteration
StopIteration
异常用于标识迭代的完成,防止出现无限循环的情况,在 __next__()
方法中我们可以设置在完成指定循环次数后触发 StopIteration
异常来结束迭代。
if self.current >= self.limit: | |
# raise: 抛出异常,并终止程序运行的. | |
raise StopIteration # 抛出异常,StopIteration: 迭代结束. | |
# 默认抛出的 StopIteration 不携带信息,如果想携带,使用如下示例: | |
# raise StopIteration (' 达到了迭代限制。') |
# 生成器
生成器是 Python 中的一种特殊类型的迭代器,它通常由包含 yield
语句的函数来定义。与常规函数不同,生成器不会一次性返回所有结果,而是返回一个迭代器对象。
当在生成器函数中使用 yield
语句时,函数的执行将会暂停,并将 yield
后面的表达式作为当前迭代的值返回。
然后,每次调用生成器的 next ()
方法或使用 for
循环进行迭代时,函数会从上次暂停的地方继续执行,直到再次遇到 yield
语句。这样,生成器函数可以逐步产生值,而不需要一次性计算并返回所有结果。
调用一个生成器函数,返回的是一个迭代器对象。
# yield 关键字
使用 yield 是生成器常用的写法,yield 会记录每个生成的数据,然后逐个的放到生成器对象中,最终返回生成器对象
yield
关键字实际上在幕后管理了迭代的状态,它使得生成器函数能够在每次返回时暂停执行,直到下一次 next ()
被调用时继续。
def get_generator (): | |
for i in range (1, 6): | |
yield i | |
my_generator = get_generator () | |
# 遍历生成器,获取每个元素. | |
for i in my_generator: | |
print (i) |
# 生成器推导式
回顾之前的列表推导式,集合推导式
# 生成 1 ~ 5 的数据. | |
my_list = [i for i in range (1, 6)] | |
print (my_list, type (my_list)) # [1, 2, 3, 4, 5] <class 'list'> | |
my_set = {i for i in range (1, 6)} | |
print (my_set, type (my_set)) # {1, 2, 3, 4, 5} <class'set'> |
尝试写一下,"元组" 推导式,发现打印的结果不是元组,而是对象,因为这种写法就是生成器推导式
# 生成器推导式 | |
my_tuple = (i for i in range (1, 6)) | |
print (my_tuple) # <generator object <genexpr> at 0x0000024C90F056D0> 生成器对象 | |
print (type (my_tuple)) # <class 'generator'> 生成器类型 | |
# 从生成器中获取数据. | |
# 格式 1: for 循环遍历 获取全部 | |
for i in my_tuple: | |
print (i) | |
# 格式 2: next () 函数,逐个获取. | |
print (next (my_tuple)) # 1 | |
print (next (my_tuple)) # 2 |
# 区别与联系
# 区别
- 内存效率:生成器比迭代器更加内存友好。迭代器可能会一次性加载所有数据到内存中,而生成器则是按需生成数据。
- 使用场景:迭代器通常用于遍历已存在的集合,而生成器则用于生成可能无限大的序列,或者在需要时才计算值的情况。
- 生命周期:生成器只能被遍历一次,因为它们在每次迭代之后会保留状态,一旦到达序列的末尾就会抛出 StopIteration 异常并关闭自身。而迭代器理论上可以被多次遍历,尽管实际上许多迭代器设计为只遍历一次。
- 创建方式:迭代器可以通过实现特定接口来创建,而生成器通常是通过带有 yield 语句的函数来创建。
# 联系
- 都是迭代协议的实现:无论是迭代器还是生成器,它们都遵循迭代协议,即实现了
__iter__
和__next__
方法。 - 都可以被迭代:由于生成器本身就是迭代器的一种,所以它们都可以使用 for 循环或其他迭代机制来访问。
- 都支持懒加载:两者都支持懒加载模式,即在需要时才生成数据,这对于处理大数据集尤其有用。
# 内置的迭代器函数
Python 提供了一些常用的迭代器函数,使得迭代更方便:
- iter ():将可迭代对象转化为迭代器。
- next ():返回迭代器的下一个元素。
- zip ():将多个可迭代对象 “打包” 成一个迭代器。
- enumerate ():返回一个包含索引和元素的迭代器。
- map () 和 filter ():对可迭代对象进行函数操作。