PYTHON迭代器与生成器

阅读 1

9小时前

Python 中的迭代器(Iterator)和生成器(Generator)是 Python 中处理序列数据的核心概念,对于编写高效、优雅的代码至关重要。

1. 核心概念:可迭代对象、迭代器与生成器

要理解生成器,首先要理解迭代器;而要理解迭代器,首先要理解可迭代对象。这三者的关系是层层递进的。

| 概念 | 定义 | 关键方法或特征 |
| :--- | :--- | :--- |
可迭代对象 (Iterable) | 一个可以返回迭代器的对象。 它是数据的源头。 | 实现了 __iter__() 方法。 |
迭代器 (Iterator) | 一个同时实现了 __iter__() 和 __next__() 方法的对象。 它是数据的生产者,负责产生值并记录当前状态。 | 实现了 __iter__() (返回自身) 和 __next__() (返回下一个值) 方法。 |
生成器 (Generator) | 一种特殊的迭代器,由生成器函数或生成器表达式创建。 它提供了一种更简洁、优雅的构建迭代器的方式。 | 使用 yield 关键字或生成器表达式 (x for x in ...)。 |

简单比喻:

  • 可迭代对象就像一本书(数据源)。
  • 迭代器就像一个书签(记录你读到哪里,并知道如何获取下一页)。
  • 生成器是一个“智能书签”,它不仅能记录位置,还能按需“打印”出下一页的内容(动态生成值)。

2. 可迭代对象 (Iterable)

我们日常接触的绝大多数序列都是可迭代对象。

常见例子: listtuplestrdictsetfile 等。

工作原理:
当你使用 for item in my_list: 循环时,Python 内部会:

  1. 调用 my_list.__iter__() 方法,获取一个迭代器对象。
  2. 重复调用这个迭代器对象的 __next__() 方法,将返回值赋给 item
  3. 直到 __next__() 抛出 StopIteration 异常,循环结束。

你可以用 iter() 函数手动获取一个可迭代对象的迭代器:

my_list = [1, 2, 3]
iterator = iter(my_list) # 等同于 iterator = my_list.__iter__()

print(next(iterator)) # 输出 1 (等同于 iterator.__next__())
print(next(iterator)) # 输出 2
print(next(iterator)) # 输出 3
print(next(iterator)) # 抛出 StopIteration 异常

3. 迭代器 (Iterator)

迭代器是“值流”的抽象。它一定是可迭代的(因为它有 __iter__),但可迭代对象不一定是迭代器(例如列表不是迭代器,但它的 __iter__() 返回的才是)。

如何创建自定义迭代器?
创建一个类,实现 __iter__() 和 __next__() 方法。

示例:创建一个返回数字的迭代器,直到达到最大值

class MyCounter:
    def __init__(self, max_value):
        self.max = max_value
        self.current = 0

    def __iter__(self):
        # __iter__ 必须返回一个迭代器对象,因为它自己就是迭代器,所以返回 self
        return self

    def __next__(self):
        if self.current < self.max:
            self.current += 1
            return self.current
        else:
            # 必须抛出 StopIteration 来终止迭代
            raise StopIteration

# 使用
counter = MyCounter(3)
for num in counter:
    print(num)
# 输出:
# 1
# 2
# 3

缺点: 用类来实现迭代器比较繁琐,需要管理状态(如 self.current)和异常。这就是生成器出现的原因。

4. 生成器 (Generator)

生成器是创建迭代器的“语法糖”,它让代码变得异常简洁。

方式一:生成器函数 (Generator Function)

使用 def 定义函数,但在函数体中使用 yield 关键字而不是 return

yield 的关键行为:

  1. 当函数被调用时,它不会立即执行,而是返回一个生成器对象(一种迭代器)。
  2. 当第一次调用 next() 时,函数从开头开始执行,直到遇到 yield 语句。yield 会暂停函数的执行,并将后面的值返回给调用者。
  3. 下次再调用 next(),函数会从上次 yield 语句之后的位置恢复执行,直到再次遇到 yield
  4. 如果函数执行到 return 语句或函数结尾,会抛出 StopIteration 异常。

示例:用生成器函数实现上面的 MyCounter

def my_counter_generator(max_value):
    current = 0
    while current < max_value:
        current += 1
        yield current # 在此处暂停,返回 current 的值

# 使用
counter_gen = my_counter_generator(3) # 此时函数并未执行,只是创建了生成器对象
print(next(counter_gen)) # 输出 1 (函数执行到 yield current 处暂停)
print(next(counter_gen)) # 输出 2 (从 yield 之后恢复,继续 while 循环)
print(next(counter_gen)) # 输出 3
print(next(counter_gen)) # 抛出 StopIteration

# 更常见的用法是用于 for 循环
for num in my_counter_generator(3):
    print(num)

方式二:生成器表达式 (Generator Expression)

语法与列表推导式类似,但使用圆括号 () 而不是方括号 []

  • 列表推导式: [x*x for x in range(5)] → [0, 1, 4, 9, 16] (立即计算所有结果,占用内存)
  • 生成器表达式: (x*x for x in range(5)) → 生成器对象 (惰性计算,节省内存)

# 生成器表达式
gen_exp = (x * x for x in range(5))
print(gen_exp) # <generator object <genexpr> at 0x...>
for i in gen_exp:
    print(i) # 输出 0, 1, 4, 9, 16

# 可以直接作为函数参数
sum_of_squares = sum(x*x for x in range(5))
print(sum_of_squares) # 输出 30

5. 核心优势与使用场景

  1. 惰性计算 (Lazy Evaluation)这是最重要的优势。生成器不会一次性生成所有数据并存入内存,而是按需生成一个值、处理一个值。这对于处理大规模甚至无限的数据流至关重要。
  • 场景: 读取几个 GB 的日志文件,使用生成器可以逐行读取处理,而不是一次性加载到内存导致崩溃。
  1. 代码更简洁:用几行生成器代码就能替代一个复杂的迭代器类。
  2. 表示无限序列:由于是惰性的,可以轻松表示无限序列。

def infinite_sequence():
    num = 0
    while True:
        yield num
        num += 1

# 使用
for i in infinite_sequence():
    if i > 100:
        break
    print(i)

  1. 协同工作与管道 (Pipeline):多个生成器可以连接起来,形成高效的数据处理管道。

# 一个简单的管道示例:求0-99中所有偶数的平方和
numbers = (x for x in range(100))        # 生成器1:产生数字
even_numbers = (x for x in numbers if x % 2 == 0) # 生成器2:过滤偶数
squared = (x*x for x in even_numbers)    # 生成器3:计算平方
result = sum(squared)                    # 求和
print(result)

数据像水流一样逐个通过每个生成器,内存中始终只有当前处理的数据。

总结与对比

| 特性 | 迭代器 (Iterator) | 生成器 (Generator) |
| :--- | :--- | :--- |
实现方式 | 通过类实现 __iter__ 和 __next__ | 通过函数 + yield 或生成器表达式 |
代码简洁性 | 相对繁琐,需手动管理状态 | 极其简洁,状态由函数暂停/恢复自动管理 |
内存效率 | 高(惰性计算) | 高(惰性计算) |
功能强度 | 基础,可定义复杂逻辑 | 更专注于“按需生成值”这一场景 |
关系 | 生成器是一种特殊的迭代器 | 所有生成器都是迭代器 |

简单决策:当你需要创建一个自定义的迭代器时,99% 的情况都应该首选生成器。 它用更少的代码实现了同样的功能,并且更加 Pythonic。

精彩评论(0)

0 0 举报