在 Python 中,迭代是其最强大的功能之一,它允许我们以一种统一的方式访问集合中的每一个元素。本文将深入探讨 Python 中的两种核心迭代机制:迭代器(Iterator) 和 生成器(Generator)。我们将详细介绍它们的概念、工作原理、如何创建和使用它们,并通过丰富的代码示例来加深理解。迭代器是一个可以记住遍历位置的对象。它从集合的第一个元素开始访问,直到所有元素被访问完毕。迭代器的一个关键特性是它只能向前移动,不能后退。1.1 迭代器的基本方法:iter() 和 next()Python 中,任何可迭代对象(如列表、元组、字符串)都可以通过 iter() 函数来创建一个迭代器。然后,我们可以使用 next() 函数来逐个访问迭代器中的元素。# 创建一个列表my_list = [1, 2, 3, 4]# 从列表创建一个迭代器my_iterator = iter(my_list)# 使用 next() 访问元素print(next(my_iterator)) # 输出: 1print(next(my_iterator)) # 输出: 2
当迭代器中没有更多元素时,再次调用 next() 会引发一个 StopIteration 异常。for 循环在后台自动处理了 iter() 和 next() 的调用以及 StopIteration 异常。这是遍历迭代器最常见和推荐的方式。my_list = [1, 2, 3, 4]my_iterator = iter(my_list)for item in my_iterator: print(item, end=" ") # 输出: 1 2 3 4
要创建一个自定义的迭代器,我们需要在一个类中实现两个特殊方法:__iter__() 和 __next__()。
示例:创建一个生成从1开始的数字的迭代器
class MyNumbers: def __iter__(self): self.a = 1 return self def __next__(self): x = self.a self.a += 1 return xmy_class = MyNumbers()my_iter = iter(my_class)print(next(my_iter)) # 输出: 1print(next(my_iter)) # 输出: 2
1.4 使用 StopIteration 结束迭代
为了防止无限循环,__next__() 方法必须在迭代完成时引发 StopIteration 异常。我们可以通过添加一个条件来控制迭代的次数。class MyNumbers: def __iter__(self): self.a = 1 return self def __next__(self): if self.a <= 20: x = self.a self.a += 1 return x else: raise StopIterationmy_class = MyNumbers()my_iter = iter(my_class)for x in my_iter: print(x)
生成器是创建迭代器的更简单、更优雅的方式。在 Python 中,任何使用了 yield 关键字的函数都称为生成器函数。
yield 的工作方式类似于 return,但它会保存函数的状态。当再次调用生成器时,它会从上次离开的地方继续执行。
调用一个生成器函数会返回一个迭代器对象,然后我们可以像使用普通迭代器一样使用它。
示例:一个简单的倒计时生成器
def countdown(n): while n > 0: yield n n -= 1# 创建生成器对象generator = countdown(5)# 使用 next() 获取值print(next(generator)) # 输出: 5print(next(generator)) # 输出: 4# 使用 for 循环迭代for value in generator: print(value) # 输出: 3 2 1
2.2 生成器的优势
生成器的主要优势在于内存效率。它们是“懒惰”的,按需生成值,而不是一次性在内存中创建整个序列。这使得它们在处理大数据集或无限序列时非常有用。
示例:使用生成器实现斐波那契数列
def fibonacci(n): a, b, counter = 0, 1, 0 while counter < n: yield a a, b = b, a + b counter += 1f = fibonacci(10)for num in f: print(num, end=" ") # 输出: 0 1 1 2 3 5 8 13 21 34
创建可迭代对象: 首先,有一个可迭代的数据集合,如列表、元组或字符串。
获取迭代器: 使用 iter() 函数从可迭代对象中获取一个迭代器对象。
循环/调用 next():
1.for 循环: for 循环开始,它会自动调用迭代器的 __iter__() 方法(虽然对于迭代器本身来说,这只是返回 self)。
2.在循环的每一次迭代中,for 循环内部会调用迭代器的 __next__() 方法来获取下一个元素。
3.手动调用: 或者,你可以手动重复调用 next() 函数来获取元素。
4.处理元素: 对从 __next__() 或 next() 获取的元素执行所需的操作。
检测 StopIteration:
1.for 循环: 当 __next__() 方法引发 StopIteration 异常时,for 循环会自动捕获这个异常并优雅地退出循环。
2.手动调用: 如果是手动调用 next(),你需要使用 try...except StopIteration 块来捕获异常并处理迭代结束的情况。
迭代结束: 循环或手动调用过程结束。
3.2 生成器逻辑流程
定义生成器函数: 创建一个包含 yield 关键字的函数。
创建生成器对象: 调用生成器函数。这不会立即执行函数体,而是返回一个生成器对象(它本身就是一个迭代器)。
开始迭代:
1.for 循环: 当 for 循环开始遍历生成器对象时,它会触发生成器函数的执行。
2.手动调用 next(): 第一次调用 next() 也会触发生成器函数的执行。
执行到 yield: 函数代码从头开始执行,直到遇到第一个 yield 语句。
产出值并暂停: yield 语句会产出一个值(这个值成为 for 循环当前迭代的项或 next() 的返回值),然后函数的执行状态被冻结(所有局部变量和指令指针都被保存),并暂停。
继续迭代:
1.for 循环: 在下一次迭代中,for 循环会再次“唤醒”生成器函数。
2.手动调用 next(): 下一次调用 next() 也会“唤醒”函数。
从暂停处恢复: 函数从上次 yield 语句之后的地方继续执行,直到遇到下一个 yield 语句,然后重复第5步。
函数结束或 return: 如果函数执行到末尾,或者遇到一个 return 语句(在Python 3.3+中,生成器中的 return 会引发 StopIteration),生成器会引发一个 StopIteration 异常。
迭代结束: for 循环捕获 StopIteration 并退出。如果是手动调用,你需要自己处理这个异常。
结论
迭代器和生成器是 Python 中用于处理序列数据的强大工具。迭代器提供了一种通用的、内存高效的访问集合元素的方式,而生成器则提供了一种更简洁、更强大的语法来创建迭代器。理解并熟练运用它们,将极大地提升你的 Python 编程效率和代码质量。