for 循环看起来简单,背后却有一套完整的协议在支撑。理解它,你就能让任何自定义对象都能被 for 遍历。迭代器协议:for 循环的底层原理
Python 中的迭代协议只要求两个方法:
classMyIterator:def__iter__(self):returnself# 返回迭代器本身def__next__(self):# 返回下一个值,没有时抛出 StopIteration ...
for 循环的本质就是:
手动实现一个迭代器
classCountdown:def__init__(self, start):self.current = startdef__iter__(self):returnselfdef__next__(self):ifself.current <= 0:raise StopIteration value = self.currentself.current -= 1return valuefor n in Countdown(5):print(n)# 5, 4, 3, 2, 1
这就是一个倒计时迭代器。每次 __next__ 返回当前值并减1,到0时抛出 StopIteration 结束。
生成器:更简单的迭代器
写 __iter__ 和 __next__ 有点麻烦?用 yield 关键字:
defcountdown(start): current = startwhile current > 0:yield current # 暂停并返回当前值 current -= 1for n in countdown(5):print(n)# 5, 4, 3, 2, 1
yield 让函数变成生成器——它自动实现了 __iter__ 和 __next__,代码量减少一半。
yield 的工作原理
defsimple_gen():print("开始")yield1print("继续")yield2print("结束")g = simple_gen() # 此时函数还没执行!print(next(g)) # 输出"开始",返回 1print(next(g)) # 输出"继续",返回 2print(next(g)) # 输出"结束",抛出 StopIteration
关键点:
- 每次
next() 执行到下一个 yield 处暂停
让自定义对象可遍历
classTeam:def__init__(self):self.members = ["Alice", "Bob", "Charlie"]def__iter__(self):returniter(self.members)# 或者用生成器:# for member in self.members:# yield memberteam = Team()for person in team:print(person)# Alice, Bob, Charlie
只需要实现 __iter__,返回一个迭代器即可。
生成器表达式
类似列表推导式,但用圆括号,按需生成不占内存:
# 列表推导式:一次性生成所有值squares_list = [x*x for x inrange(1000000)] # 占用大量内存# 生成器表达式:每次只生成一个值squares_gen = (x*x for x inrange(1000000)) # 几乎不占内存print(next(squares_gen)) # 0print(next(squares_gen)) # 1print(next(squares_gen)) # 4
生成器的进阶用法
无限序列
deffibonacci(): a, b = 0, 1whileTrue:yield a a, b = b, a + bfib = fibonacci()for _ inrange(10):print(next(fib), end=" ")# 0 1 1 2 3 5 8 13 21 34
管道式处理
defread_lines(filename):withopen(filename) as f:for line in f:yield line.strip()deffilter_empty(lines):for line in lines:if line:yield linedefto_upper(lines):for line in lines:yield line.upper()# 链式处理,每一步都是惰性求值pipeline = to_upper(filter_empty(read_lines("data.txt")))for line in pipeline:print(line)
小结
| |
|---|
__iter__ | |
__next__ | 返回下一个值,结束时抛出 StopIteration |
yield | |
| (x for x in range(10)) |
| |
💡 练习:写一个 range2(start, stop, step) 生成器,模拟内置 range() 的功能。