不知道你有没有过这种经历:
看Python教程碰到迭代器就头大,翻生成器文档更是越看越懵。 明明for循环用得好好的,为啥还要搞这么多复杂概念?
其实这俩特性一点都不抽象,反而能帮你解决很多实际问题。 今天咱们就用最通俗的方式,把它俩彻底讲明白。
🔍 一、先搞懂迭代器是什么
先举个生活里的例子:你买了一杯珍珠奶茶,插了根吸管。
你每吸一口,就能喝到一颗珍珠,直到吸完为止。 这根吸管不用提前把所有珍珠都舀出来,每次只取当前的一颗,这就是迭代器的核心逻辑。
📌 迭代器的两个核心方法
__iter__():返回迭代器本身,相当于告诉Python「我是个可以迭代的对象」__next__():返回下一个元素,没有元素就抛出StopIteration异常
我们来写个最简单的迭代器,模拟数1到3的过程:
class Counter: def __init__(self, max_num): self.max = max_num self.current = 1 def __iter__(self): return self # 返回迭代器本身 def __next__(self): if self.current > self.max: raise StopIteration # 没有元素了就停止 num = self.current self.current += 1 return num # 使用迭代器 counter = Counter(3) for num in counter: print(num) # 输出 1 2 3
发现没?你平常用的for循环遍历列表、字典,底层其实就是自动调用了迭代器的这两个方法。
迭代器最大的好处是:不用一次性把所有元素加载到内存,特别适合处理超大文件或者无限序列。
💡 二、生成器:简化版的迭代器
刚才写迭代器还要定义类、实现两个方法,是不是有点麻烦?
生成器就是Python给我们提供的「迭代器快捷写法」,不用写类,只要用yield关键字就行。
📌 yield关键字的作用
普通函数用return返回结果,返回之后函数就结束了。
但用yield的函数,返回的是一个生成器对象,每次调用next()的时候,执行到yield就暂停,下次调用再从暂停的地方继续执行。
我们用生成器重写刚才的计数器,代码直接少了一半:
def counter(max_num): current = 1 while current <= max_num: yield current # 遇到yield就暂停,返回当前值 current += 1 # 使用生成器 for num in counter(3): print(num) # 输出 1 2 3
是不是超级简洁?你不用再手动实现__iter__和__next__方法,Python会自动帮你处理。
⚡ 更简单的生成器表达式
如果你只需要简单的生成器,还可以用类似列表推导式的写法,把方括号换成圆括号就行:
# 生成1到10的平方数 gen = (x*x for x in range(1, 11)) # 遍历生成器 for num in gen: print(num)
注意:生成器只能遍历一次,遍历完就空了,想要再次使用需要重新创建。
🎯 实际场景用起来
说了这么多,这俩东西到底能解决什么实际问题?给你举两个最常用的场景。
📌 场景1:读取超大日志文件
如果有一个10G的日志文件,你直接用readlines()读取的话,内存直接就爆了。用生成器就可以一行一行读取,内存占用永远只有几KB:
def read_large_log(file_path): with open(file_path, 'r', encoding='utf-8') as f: for line in f: yield line.strip() # 每次返回一行 # 遍历日志,只打印包含error的行 for line in read_large_log('access.log'): if 'error' in line: print(line)
📌 场景2:生成无限序列
比如你需要生成斐波那契数列,不需要提前算好所有数,用生成器可以无限生成,需要多少取多少:
def fibonacci(): a, b = 0, 1 while True: yield a a, b = b, a + b # 取前10个斐波那契数 fib = fibonacci() for _ in range(10): print(next(fib)) # 输出 0 1 1 2 3 5 8 13 21 34
📝 最后总结一下
- 迭代器是实现了__iter__和__next__方法的对象,惰性计算,省内存
- 生成器是简化版的迭代器,用yield关键字定义,代码更简洁
你在项目里用过迭代器或者生成器吗?欢迎在评论区分享你的使用场景~