《深入 Python 可迭代世界:三种方式手写斐波那契数列(类、生成器、itertools)全解析》
在我这些年教授 Python 的过程中,有一个经典问题几乎从未缺席过课堂与面试——如何实现一个可迭代的斐波那契数列?
它看似简单,却能从多个维度考察一个 Python 开发者对:
- 迭代器协议
- 生成器机制
- 惰性计算
- itertools 工具链
- 代码风格与可维护性
的理解程度。
更重要的是,它能帮助你真正理解 Python “让数据流动起来”的哲学。
今天,我们就从基础到进阶,带你用三种方式手写一个可迭代的 Fibonacci:
- 基于类的迭代器实现
- 基于生成器的实现
- 基于 itertools 的函数式实现
并结合实战经验,分析每种方式的适用场景、优缺点与最佳实践。
一、开篇:为什么是斐波那契?为什么是可迭代?
Python 的发展史是一部“简洁优雅”的进化史。从 Guido 在 1989 年圣诞节写下第一行 Python 代码,到如今成为 Web、数据科学、AI、自动化的主流语言,它的核心理念始终未变:
让复杂的事情变简单,让简单的事情变优雅。
而“可迭代”正是 Python 世界中最优雅的抽象之一。
- 你可以遍历一个列表
- 你可以遍历一个文件
- 你可以遍历一个无限序列(如 Fibonacci)
- 你可以遍历一个网络流、数据库游标、实时数据管道
可迭代对象是 Python 数据流的基础设施。
而 Fibonacci 则是最适合作为示例的序列:
- 简单易懂
- 具备数学美感
- 可以无限生成
- 适合展示惰性计算
- 适合展示迭代器协议与生成器协议
所以,这篇文章不仅仅是“写 Fibonacci”,而是带你真正理解 Python 的迭代哲学。
二、基础回顾:迭代器协议与生成器协议
在进入代码之前,我们先快速回顾两个核心概念。
1. 迭代器协议(Iterator Protocol)
只要一个对象实现了:
__iter__()__next__()
它就是一个迭代器。
class Iterator:
def __iter__(self):
return self
def __next__(self):
...
2. 生成器协议(Generator Protocol)
带 yield 的函数会自动生成一个实现了:
__iter__()__next__()send()throw()close()
的生成器对象。
生成器是 Python 最优雅的状态机。
三、第一种写法:基于类的迭代器(最原始、最底层、最能体现协议本质)
这是最“正统”的写法,也是面试中最能体现你对迭代器协议理解程度的方式。
1. 代码实现
class Fibonacci:
def __init__(self, max_count=None):
self.a = 0
self.b = 1
self.count = 0
self.max_count = max_count # 可选:限制生成数量
def __iter__(self):
return self
def __next__(self):
if self.max_count is not None and self.count >= self.max_count:
raise StopIteration
value = self.a
self.a, self.b = self.b, self.a + self.b
self.count += 1
return value
2. 使用方式
for n in Fibonacci(10):
print(n)
输出:
0 1 1 2 3 5 8 13 21 34
3. 优点
- 完整体现迭代器协议
- 可自定义状态、限制、缓存等逻辑
- 适合构建复杂迭代器类
4. 缺点
- 代码冗长
- 手动维护状态容易出错
- 不够 Pythonic
5. 适用场景
- 面试
- 需要构建复杂迭代器类
- 需要可复用、可扩展的对象
四、第二种写法:基于生成器(最 Pythonic、最常用、最优雅)
生成器是 Python 的灵魂之一。
只需要一个 yield,你就能写出一个可迭代的 Fibonacci。
1. 代码实现
def fibonacci(max_count=None):
a, b = 0, 1
count = 0
while max_count is None or count < max_count:
yield a
a, b = b, a + b
count += 1
2. 使用方式
for n in fibonacci(10):
print(n)
3. 优点
- 代码最简洁
- 自动维护状态
- 惰性计算
- 可无限生成
4. 缺点
- 不适合复杂状态机
- 不如类迭代器可扩展
5. 适用场景
- 90% 的实际项目
- 数据流处理
- pipeline 设计
- 惰性计算
五、第三种写法:基于 itertools(函数式、组合式、最强大)
itertools 是 Python 中最强大的函数式工具库之一。
我们可以用它构建一个无限 Fibonacci 序列。
1. 使用 itertools.count + accumulate
import itertools
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
但这只是生成器写法。
我们来点更“itertools 风格”的。
2. 使用 itertools.accumulate(更函数式)
import itertools
import operator
def fibonacci():
return itertools.accumulate(
itertools.repeat(1),
lambda x, _: (x[1], x[0] + x[1]),
initial=(0, 1)
)
但这个版本返回的是 (a, b) 元组,我们需要取第一个值:
def fibonacci():
for a, b in itertools.accumulate(
itertools.repeat(1),
lambda x, _: (x[1], x[0] + x[1]),
initial=(0, 1)
):
yield a
3. 使用方式
fib = fibonacci()
for _ in range(10):
print(next(fib))
4. 优点
- 完全惰性
- 可组合、可复用
- 适合函数式编程风格
- 与 pipeline 完美结合
5. 缺点
- 可读性较差
- 不适合初学者
6. 适用场景
- 高性能数据流处理
- pipeline 组合
- 需要与 itertools 生态结合
六、三种方式对比总结
| 写法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 类迭代器 | 完整体现协议、可扩展 | 代码冗长 | 面试、复杂状态机 |
| 生成器 | 最简洁、最 Pythonic | 扩展性一般 | 90% 项目、数据流 |
| itertools | 高性能、可组合 | 可读性差 | pipeline、函数式编程 |
七、实战:构建一个 Fibonacci 数据处理 pipeline
假设我们要:
- 生成 Fibonacci 序列
- 过滤出偶数
- 取前 10 个
- 求平方
我们可以用生成器 pipeline:
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
def even_filter(seq):
for n in seq:
if n % 2 == 0:
yield n
def square(seq):
for n in seq:
yield n * n
def take(seq, n):
for i, v in enumerate(seq):
if i >= n:
break
yield v
pipeline = take(square(even_filter(fibonacci())), 10)
print(list(pipeline))
输出:
[0, 4, 64, 1156, 20736, ...]
这就是 Python 惰性数据流的魅力。
八、最佳实践:如何选择合适的实现方式?
1. 如果你在写教程或面试题:用类迭代器
能展示你对协议的理解。
2. 如果你在写实际项目:用生成器
最简洁、最优雅、最易维护。
3. 如果你在写数据处理 pipeline:用 itertools
性能最佳、组合性最强。
九、前沿视角:可迭代思想在现代 Python 中的延伸
Python 的可迭代思想已经渗透到:
- asyncio(异步迭代器)
- FastAPI(流式响应)
- Pandas(迭代行)
- PyTorch(数据加载器 DataLoader)
- 生成器协程(PEP 380)
- async generator(PEP 525)
未来,随着数据规模不断扩大,惰性计算与可迭代数据流将变得更加重要。
十、总结与互动
今天,我们从基础到进阶,完整解析了:
- Python 的迭代器协议与生成器协议
- 三种方式手写 Fibonacci
- 各自的优缺点与适用场景
- 如何构建数据流 pipeline
- 可迭代思想在现代 Python 中的延伸
我很想听听你的想法:
- 你更喜欢哪种 Fibonacci 写法?
- 你在项目中是否用过生成器或 itertools?
- 想不想让我写一篇《深入 itertools:构建高性能数据流处理框架》?
欢迎留言,我们一起把 Python 玩得更深入、更优雅。
如果你愿意,我也可以继续写:
- 迭代器协议的底层实现
- yield from 与 await 的关系
- 如何用生成器构建协程系统
随时告诉我,我很乐意继续陪你探索 Python 的世界。

8149

被折叠的 条评论
为什么被折叠?



