学习 Python迭代器与生成器的至强指南

学习 Python 的过程中,你可能会听到“迭代器(iterator)”和“可迭代对象(iterable)”这两个术语。

它们听起来很像,意思也有点接近,但其实是不同的概念。

今天就带你搞清楚它们到底是什么,并用一些生活中的例子帮助你理解。

图片

什么是迭代器?

迭代器是可以逐步取出数据的对象。它们的主要特点是记得上一次取到哪里,并能继续往下取。

我们来看看一个简单的例子:

people = ["小明", "小红", "小刚"]  # 定义一个包含三个名字的列表

这是一个包含三个人名的列表。如果想把它变成迭代器,只需要用 Python 自带的 iter() 函数:

people_iterator = iter(people)  # 使用 iter() 将列表转化为迭代器

现在,people_iterator 就是一个迭代器了。

那么迭代器有什么特别之处呢?

迭代器的特点

  1. 1. 按需取数据
    迭代器不会一次性加载所有数据,而是需要的时候再取。例如:

    print(next(people_iterator))  # 输出 "小明"
    print(next(people_iterator))  # 输出 "小红"

    每次调用 next(),都会返回下一个值。

  2. 2. 不可回头
    迭代器是一次性的,一旦取出一个元素,就不能再返回。例如:

    print(list(people_iterator))  # 输出 ["小刚"],迭代器剩余的部分被转为列表
    print(list(people_iterator))  # 输出 [],因为迭代器已经用完了
  3. 3. 节省内存
    因为迭代器不会一次性加载所有数据,所以它特别适合处理大量数据。想象一下,如果你要处理一百万条数据,而不是一次性加载到内存中,迭代器会让程序运行得更高效。

StopIteration 异常

当迭代器中的数据取完后,再调用 next(),会抛出一个特殊的错误:

try:
    print(next(people_iterator))  # 再次调用 next(),但迭代器已耗尽
except StopIteration:  # 捕获 StopIteration 异常
    print("没有数据了!")
 

什么是生成器?

生成器是创建迭代器的一种简单方法。只需要用 yield 关键字,就可以轻松写出一个生成器函数:

def generate_numbers(n):
    for i in range(n):  # 循环生成从 0 到 n-1 的数字
        yield i  # 使用 yield 暂停函数执行并返回值

调用这个函数会得到一个生成器对象:

gen = generate_numbers(3)  # 创建生成器对象
print(next(gen))  # 输出 0
print(next(gen))  # 输出 1

生成器其实就是特殊的迭代器。也就是说,所有生成器都是迭代器,但并不是所有迭代器都是生成器

生成器还有一个特别的地方,就是它能让代码更简洁。

例如,生成一个斐波那契数列:

def fibonacci(n):
    a, b = 0, 1  # 初始化斐波那契数列的前两个数字
    for _ in range(n):  # 循环生成 n 个数字
        yield a  # 返回当前的数字
        a, b = b, a + b  # 更新为下一个斐波那契数

使用生成器生成并输出前 10 个斐波那契数:

for num in fibonacci(10):
    print(num)

你会发现,生成器让复杂的逻辑看起来清晰易懂。

什么是可迭代对象?

可迭代对象是可以用来创建迭代器的对象

常见的可迭代对象有:列表、元组、集合、字符串、字典等。

只要一个对象能用 iter() 函数转成迭代器,它就是可迭代的。例如:

my_list = [1, 2, 3]  # 定义一个列表
iterator = iter(my_list)  # 使用 iter() 将列表转化为迭代器

但有些东西,比如整数,是不可迭代的:

num = 42  # 整数类型
iterator = iter(num)  # 会报错:TypeError,因为整数不可迭代

判断是否是可迭代对象

判断一个对象是否可迭代有一个简单的方法,就是看它能不能用 for 循环。比如:

for char in "hello":  # 遍历字符串的每个字符
    print(char)  # 每次输出一个字母

如果一个对象能用 for 循环,那它一定是可迭代的。

迭代器和可迭代对象的区别

  1. 1. 核心区别
    • • 可迭代对象是一个“容器”,可以用来生成迭代器。
    • • 迭代器是一个可以逐步返回数据的“工具”。
  2. 2. 使用方式
    • • 可迭代对象可以直接用 for 循环:

      for name in ["小明", "小红", "小刚"]:  # 遍历列表
          print(name)
    • • 迭代器需要用 next()

      names = iter(["小明", "小红", "小刚"])  # 将列表转换为迭代器
      print(next(names))  # 输出第一个元素 "小明"
    •  

迭代器和生成器的实际用法

  1. 1. 逐行读取大文件
    如果我们需要处理一个很大的文件,可以用生成器按需读取:

    def read_large_file(file_path):
        with open(file_path) as file:  # 打开文件
            for line in file:  # 按行读取文件内容
                yield line.strip()  # 去掉行末的换行符并返回

    这种方法只会一次读取一行,避免内存不足。

  2. 2. 自定义迭代器
    如果需要更复杂的迭代逻辑,可以自己写一个迭代器类:

    class Counter:
        def __init__(self, start, end):  # 初始化起始值和结束值
            self.current = start
            self.end = end
    
        def __iter__(self):  # 定义可迭代协议
            return self
    
        def __next__(self):  # 定义如何生成下一个值
            if self.current > self.end:
                raise StopIteration  # 数据用完时抛出异常
            value = self.current
            self.current += 1  # 增加当前值
            return value  # 返回当前值

    使用自定义迭代器:

    counter = Counter(1, 5)  # 创建计数器实例
    for num in counter:
        print(num)  # 输出从 1 到 5 的数字
  3. 3. 生成数学序列
    除了斐波那契数列,你还可以用生成器生成各种数学序列,比如质数序列:

    def prime_numbers(limit):
        def is_prime(num):  # 判断一个数是否是质数
            if num < 2:
                return False
            for i in range(2, int(num**0.5) + 1):  # 试除法
                if num % i == 0:
                    return False
            return True
    
        count = 0
        num = 2  # 从 2 开始寻找质数
        while count < limit:
            if is_prime(num):
                yield num  # 生成一个质数
                count += 1  # 计数加一
            num += 1  # 检查下一个数

    使用生成器生成前 10 个质数:

    for prime in prime_numbers(10):
        print(prime)
  4.  

总结

  • • 迭代器 是一种可以逐步取数据的对象,用于节省内存。
  • • 生成器 是创建迭代器的简便方法,用 yield 编写。
  • • 可迭代对象 是一种可以用来生成迭代器的容器,比如列表和元组。

通过这些知识,你不仅可以更高效地处理数据,还能更深入地理解 Python 的强大之处!

来源:猫十

THE END