迭代器和生成器是 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 ():对可迭代对象进行函数操作。