19、练习题


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)

关键实现技巧总结

  1. 惰性计算:使用yield替代return保持函数状态
  2. 内存控制:在处理流式数据时始终优先选择生成器
  3. 协程激活:首次必须调用next()send(None)
  4. 异常处理:通过throw()try/except实现健壮性
  5. 组合设计:通过管道模式构建可复用的处理链

建议读者在完成基础练习后,尝试将这些技术应用于实际项目(如日志分析、数据转换中间件等),以深化理解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wolf犭良

谢谢您的阅读与鼓励!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值