Python 生成器与协程 练习题
1. 无限斐波那契数列生成器
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
# 使用示例
gen = fibonacci()
print(next(gen)) # 0
print(next(gen)) # 1
print(next(gen)) # 1 (持续生成)
要点:
- 使用无限循环保持生成器状态
- 通过元组交换避免中间变量
2. 大文件错误行统计
def count_errors(filename):
with open(filename, 'r') as f:
for line in f:
if 'ERROR' in line:
yield 1
# 统计总错误数
error_gen = count_errors('server.log')
total_errors = sum(error_gen)
优化:使用生成器表达式更简洁
sum(1 for line in open('server.log') if 'ERROR' in line)
3. 平方值协程
def square_coroutine():
while True:
num = yield
yield num ** 2
# 使用
coro = square_coroutine()
next(coro) # 激活协程
coro.send(4) # 返回16
4. 数据清洗管道
def reader(data):
for item in data:
yield item
def filter_negative(gen):
for item in gen:
if item >= 0:
yield item
def convert_to_str(gen):
for item in gen:
yield str(item)
# 组合使用
data = [-5, 2, -3, 8]
pipeline = convert_to_str(filter_negative(reader(data)))
print(list(pipeline)) # ['2', '8']
5. 异常恢复协程
def resilient_coroutine():
while True:
try:
data = yield
print(f"处理数据: {data}")
except ValueError:
print("值错误,继续运行")
coro = resilient_coroutine()
next(coro)
coro.send(10) # 正常处理
coro.throw(ValueError) # 触发异常并恢复
6. 状态管理协程
class StatefulCoroutine:
def __init__(self):
self._state = 'stopped'
def __call__(self):
self._state = 'running'
while True:
data = yield
# 处理逻辑...
@property
def state(self):
return self._state
coro = StatefulCoroutine()
gen = coro()
next(gen)
print(coro.state) # 'running'
7. 性能对比测试
import sys, time
# 列表方式
start = time.time()
lst = [x**2 for x in range(10**6)]
print(f"列表内存: {sys.getsizeof(lst)//1024}KB")
print(f"耗时: {time.time()-start:.2f}s")
# 生成器方式
start = time.time()
gen = (x**2 for x in range(10**6))
print(f"生成器内存: {sys.getsizeof(gen)}B")
print(f"耗时: {time.time()-start:.2f}s")
典型输出:
- 列表:约8MB内存,0.15秒
- 生成器:112B内存,0.00001秒
8. 数据分片生成器
def chunk_generator(data, chunk_size):
for i in range(0, len(data), chunk_size):
yield data[i:i+chunk_size]
# 使用示例
data = bytes(1024*1024) # 1MB二进制数据
for chunk in chunk_generator(data, 1024):
process(chunk) # 每次处理1KB
9. 协程嵌套调用
def sub_coroutine():
yield from (x*2 for x in range(5))
def main_coroutine():
yield from sub_coroutine()
print(list(main_coroutine())) # [0, 2, 4, 6, 8]
10. 爬虫分页处理器
# 定义一个名为 pagination_crawler 的生成器函数,用于分页爬取网页内容
# base_url 是基础的 URL 地址,total_pages 是需要爬取的总页数
def pagination_crawler(base_url, total_pages):
# 初始化当前页码为 1
page = 1
# 当当前页码小于等于总页数时,继续循环爬取
while page <= total_pages:
# 构造当前页的完整 URL,通过在基础 URL 后添加查询参数 page
url = f"{base_url}?page={page}"
try:
# 调用 fetch_page 函数发起网络请求获取当前页的内容
# 这里假设 fetch_page 函数已经实现了网络请求的逻辑
# 使用 yield 关键字将获取到的页面内容作为生成器的一个值返回
yield fetch_page(url)
except Exception as e:
# 若在请求过程中出现异常,打印错误信息
print(f"请求第 {page} 页时出现错误: {e}")
# 当前页码加 1,准备爬取下一页
page += 1
# 假设的网络请求函数,需要根据实际情况实现
# 这里只是一个占位函数,用于模拟从指定 URL 获取页面内容
def fetch_page(url):
# 此处可以使用如 requests 库等实现实际的网络请求
# 为了示例,这里简单返回一个提示信息
return f"这是 {url} 的页面内容"
# 假设的解析函数,用于处理获取到的页面内容
# 这里只是一个占位函数,可根据实际需求实现具体的解析逻辑
def parse(page_content):
# 为了示例,这里简单打印解析信息
print(f"正在解析页面内容: {page_content}")
def test_10():
# 使用示例
for page_content in pagination_crawler("http://example.com/api", 10):
parse(page_content)
关键实现技巧总结
- 惰性计算:使用
yield
替代return
保持函数状态 - 内存控制:在处理流式数据时始终优先选择生成器
- 协程激活:首次必须调用
next()
或send(None)
- 异常处理:通过
throw()
和try/except
实现健壮性 - 组合设计:通过管道模式构建可复用的处理链
建议读者在完成基础练习后,尝试将这些技术应用于实际项目(如日志分析、数据转换中间件等),以深化理解。