Skip to content

🔄 高级魔法师修炼:生成器、迭代器和协程

欢迎来到Python异步编程的奇妙世界!在这一章节中,我们将深入探讨Python中处理数据流和异步操作的强大工具:生成器(Generators)迭代器(Iterators)协程(Coroutines)。这些特性不仅可以让你的代码更高效、更优雅,还能帮助你处理大规模数据和构建高性能的异步应用。

🎯 学习目标

通过本章的学习,你将能够:

  1. 深入理解迭代器和可迭代对象的概念与实现
  2. 熟练使用生成器函数和生成器表达式
  3. 掌握高级生成器特性和用法
  4. 理解协程的基本原理和Python异步编程模型
  5. 在实际项目中应用这些特性提高代码性能和可读性

🔍 一、迭代器的概念与实现

1.1 什么是迭代器和可迭代对象?

在Python中,我们经常使用for循环来遍历各种数据结构,如列表、元组、字典等。这背后其实隐藏着一个强大的机制:迭代器协议

  • 可迭代对象(Iterable):任何实现了__iter__()方法的对象都是可迭代的。__iter__()方法返回一个迭代器。
  • 迭代器(Iterator):任何实现了__iter__()__next__()方法的对象都是迭代器。__next__()方法返回下一个元素,如果没有更多元素,则抛出StopIteration异常。

举个例子,我们可以手动实现一个迭代器来理解这个概念:

python
class Countdown:
    """一个倒计时迭代器"""
    def __init__(self, start):
        self.start = start
    
    def __iter__(self):
        """返回自身作为迭代器"""
        return self
    
    def __next__(self):
        """返回下一个元素"""
        if self.start <= 0:
            raise StopIteration("倒计时结束")
        self.start -= 1
        return self.start + 1

# 使用我们的迭代器
countdown = Countdown(5)
for i in countdown:
    print(i)  # 输出: 5, 4, 3, 2, 1

# 尝试再次遍历(迭代器只能使用一次)
for i in countdown:
    print(i)  # 什么都不输出,因为迭代器已经用完了

1.2 iter()next()内置函数

Python提供了两个内置函数来操作迭代器:

  • iter(iterable):将可迭代对象转换为迭代器
  • next(iterator, default=None):获取迭代器的下一个元素

让我们看看如何使用这两个函数:

python
# 使用iter()和next()
numbers = [1, 2, 3, 4, 5]
number_iterator = iter(numbers)  # 获取迭代器

print(next(number_iterator))  # 输出: 1
print(next(number_iterator))  # 输出: 2
print(next(number_iterator))  # 输出: 3
print(next(number_iterator))  # 输出: 4
print(next(number_iterator))  # 输出: 5
print(next(number_iterator, "没有更多元素了"))  # 输出: "没有更多元素了"

# 尝试再次获取下一个元素(会抛出异常)
# print(next(number_iterator))  # 将抛出 StopIteration 异常

1.3 迭代器的优点

迭代器是Python的核心概念之一,它具有以下优点:

  1. 惰性计算:迭代器只在需要时才生成下一个元素,这对于处理大量数据或无限序列特别有用
  2. 内存效率:迭代器一次只存储一个元素,而不是整个序列
  3. 可组合性:可以轻松组合多个迭代器来构建复杂的数据处理管道
  4. 统一接口:Python的各种数据结构都实现了迭代器协议,提供了一致的遍历方式

🚀 二、生成器函数

2.1 什么是生成器?

生成器是一种特殊的迭代器,它使我们能够更简单地创建迭代器。生成器函数使用yield语句而不是return语句来返回值。

当调用生成器函数时,它不会执行函数体,而是返回一个生成器对象。每次调用生成器的__next__()方法时,函数体会执行到遇到的第一个yield语句,然后将yield的值返回,并暂停执行。下次调用__next__()时,函数从上次暂停的地方继续执行,直到遇到下一个yield语句或函数结束。

让我们看一个简单的生成器函数示例:

python
def countdown_generator(start):
    """一个倒计时生成器函数"""
    while start > 0:
        yield start
        start -= 1

# 使用生成器函数
countdown = countdown_generator(5)
for i in countdown:
    print(i)  # 输出: 5, 4, 3, 2, 1

# 生成器也是迭代器,所以可以使用next()函数
countdown2 = countdown_generator(3)
print(next(countdown2))  # 输出: 3
print(next(countdown2))  # 输出: 2
print(next(countdown2))  # 输出: 1
# print(next(countdown2))  # 将抛出 StopIteration 异常

2.2 生成器表达式

除了生成器函数外,Python还提供了一种更简洁的方式来创建生成器:生成器表达式。生成器表达式的语法类似于列表推导式,但使用圆括号而不是方括号。

python
# 生成器表达式示例
square_generator = (x*x for x in range(10))

# 使用生成器表达式
for square in square_generator:
    print(square)  # 输出: 0, 1, 4, 9, 16, 25, 36, 49, 64, 81

# 生成器表达式与列表推导式的区别
# 列表推导式:立即创建完整的列表
square_list = [x*x for x in range(10)]
print(type(square_list))  # 输出: <class 'list'>

# 生成器表达式:创建一个生成器对象,按需生成元素
square_generator = (x*x for x in range(10))
print(type(square_generator))  # 输出: <class 'generator'>

# 内存使用对比
import sys
print(f"列表推导式占用内存: {sys.getsizeof(square_list)} 字节")
print(f"生成器表达式占用内存: {sys.getsizeof(square_generator)} 字节")

2.3 高级生成器特性

2.3.1 send(), throw(), 和 close() 方法

生成器对象除了支持__next__()方法外,还支持以下高级方法:

  • send(value):向生成器发送一个值,并返回下一个yield的值
  • throw(type, value=None, traceback=None):向生成器抛出一个异常
  • close():关闭生成器

让我们看一个使用send()方法的例子:

python
def echo_generator():
    """一个可以接收和回显值的生成器"""
    response = yield "准备接收输入..."
    while True:
        response = yield f"你输入了: {response}"

# 创建生成器
echo = echo_generator()

# 启动生成器(第一次调用next())
first_response = next(echo)
print(first_response)  # 输出: "准备接收输入..."

# 使用send()方法发送值并获取下一个yield的值
response1 = echo.send("Hello")
print(response1)  # 输出: "你输入了: Hello"

response2 = echo.send("World")
print(response2)  # 输出: "你输入了: World"

# 关闭生成器
echo.close()

# 尝试再次使用已关闭的生成器(会抛出异常)
# echo.send("再次尝试")  # 将抛出 StopIteration 异常

2.3.2 协程与yield from

在Python 3.3中,引入了yield from语法,它允许一个生成器委托给另一个生成器、迭代器或可迭代对象。这对于构建复杂的生成器非常有用。

python
def numbers_generator():
    """生成1到5的数字"""
    for i in range(1, 6):
        yield i

def letters_generator():
    """生成A到E的字母"""
    for letter in 'ABCDE':
        yield letter

def combined_generator():
    """组合两个生成器"""
    print("生成数字:")
    yield from numbers_generator()
    print("\n生成字母:")
    yield from letters_generator()

# 使用组合生成器
for item in combined_generator():
    print(item, end=' ')
# 输出:
# 生成数字:
# 1 2 3 4 5 
# 生成字母:
# A B C D E

⚡ 三、协程与异步编程

3.1 协程的基本概念

协程是一种特殊的函数,它可以在特定点暂停执行,并在稍后恢复执行。在Python中,协程是通过async def语法定义的函数,并使用await语句来暂停执行,等待另一个协程完成。

需要注意的是,Python中的协程有一个演化过程:从早期基于生成器的协程(使用yieldyield from),到Python 3.5引入的基于async/await的现代协程。

3.2 现代协程(async/await)

Python 3.5及以上版本引入了asyncawait关键字,使协程的定义和使用更加清晰和直观。

python
import asyncio

async def say_after(delay, what):
    """在延迟指定时间后打印消息"""
    await asyncio.sleep(delay)
    print(what)

async def main():
    """主协程"""
    # 并发执行两个协程
    task1 = asyncio.create_task(say_after(1, "Hello"))
    task2 = asyncio.create_task(say_after(2, "World"))
    
    print(f"started at {asyncio.get_event_loop().time():.2f}")
    
    # 等待两个任务完成
    await task1
    await task2
    
    print(f"finished at {asyncio.get_event_loop().time():.2f}")

# 运行主协程
asyncio.run(main())
# 输出类似于:
# started at 1234567.89
# Hello
# World
# finished at 1234569.89

3.3 异步IO与事件循环

Python的异步编程基于事件循环(Event Loop)模型。事件循环负责调度和执行协程。它维护一个任务队列,不断地从队列中取出任务执行,当任务需要等待(如IO操作)时,它会暂停该任务,执行其他可执行的任务,从而实现并发。

主要的异步IO操作包括:

  1. 网络IO(HTTP请求、WebSocket连接等)
  2. 文件IO
  3. 数据库操作
  4. 定时操作

让我们看一个使用异步IO进行网络请求的例子:

python
import asyncio
import aiohttp

async def fetch(session, url):
    """异步获取URL内容"""
    async with session.get(url) as response:
        return await response.text()

async def main():
    """主协程"""
    urls = [
        'https://python.org',
        'https://github.com',
        'https://pypi.org'
    ]
    
    async with aiohttp.ClientSession() as session:
        # 并发执行多个网络请求
        tasks = [fetch(session, url) for url in urls]
        results = await asyncio.gather(*tasks)
        
        # 处理结果
        for i, result in enumerate(results):
            print(f"URL {i+1} 的内容长度: {len(result)} 字节")

# 运行主协程
asyncio.run(main())

3.4 协程的高级特性

3.4.1 任务与Future

在Python的异步编程中,TaskFuture是两个重要的概念:

  • Task:是Future的子类,表示一个正在运行的协程。你可以使用asyncio.create_task()函数来创建一个任务。
  • Future:表示一个异步操作的最终结果。它是一个低级别原语,通常你不需要直接使用它。
python
import asyncio

async def slow_operation():
    """模拟一个慢速操作"""
    await asyncio.sleep(1)
    return "操作完成"

async def main():
    """主协程"""
    # 创建任务
    task = asyncio.create_task(slow_operation())
    
    # 检查任务状态
    print(f"任务是否完成: {task.done()}")  # 输出: False
    
    # 等待任务完成并获取结果
    result = await task
    
    # 再次检查任务状态
    print(f"任务是否完成: {task.done()}")  # 输出: True
    print(f"任务结果: {result}")  # 输出: "操作完成"

# 运行主协程
asyncio.run(main())

3.4.2 异步上下文管理器

异步上下文管理器是一种特殊的上下文管理器,它定义了__aenter__()__aexit__()方法,而不是普通的__enter__()__exit__()方法。异步上下文管理器可以在async with语句中使用。

python
import asyncio

class AsyncContextManager:
    """一个简单的异步上下文管理器"""
    async def __aenter__(self):
        """进入上下文时调用"""
        print("进入异步上下文")
        await asyncio.sleep(0.1)  # 模拟异步操作
        return self
    
    async def __aexit__(self, exc_type, exc_val, exc_tb):
        """退出上下文时调用"""
        await asyncio.sleep(0.1)  # 模拟异步操作
        print("退出异步上下文")

async def main():
    """主协程"""
    async with AsyncContextManager() as acm:
        print("在异步上下文中")

# 运行主协程
asyncio.run(main())
# 输出:
# 进入异步上下文
# 在异步上下文中
# 退出异步上下文

3.4.3 异步迭代器

异步迭代器是一种特殊的迭代器,它定义了__aiter__()__anext__()方法,而不是普通的__iter__()__next__()方法。异步迭代器可以在async for语句中使用。

python
import asyncio

class AsyncCounter:
    """一个异步计数器"""
    def __init__(self, limit):
        self.limit = limit
        self.count = 0
    
    def __aiter__(self):
        """返回自身作为异步迭代器"""
        return self
    
    async def __anext__(self):
        """返回下一个元素"""
        if self.count >= self.limit:
            raise StopAsyncIteration
        self.count += 1
        await asyncio.sleep(0.1)  # 模拟异步操作
        return self.count

async def main():
    """主协程"""
    # 使用异步迭代器
    async for i in AsyncCounter(5):
        print(i)  # 输出: 1, 2, 3, 4, 5

# 运行主协程
asyncio.run(main())

💼 四、实际应用案例

4.1 生成器应用案例

4.1.1 处理大规模数据

生成器非常适合处理大规模数据,因为它们可以按需生成数据,而不需要一次性加载所有数据到内存中。

python
def read_large_file(file_path, chunk_size=1024):
    """按块读取大文件"""
    with open(file_path, 'r') as file:
        while True:
            chunk = file.read(chunk_size)
            if not chunk:
                break
            yield chunk

# 使用生成器处理大文件
for chunk in read_large_file('large_file.txt'):
    process_chunk(chunk)  # 处理每个数据块

4.1.2 数据处理管道

生成器可以组合成数据处理管道,每个生成器负责一个特定的数据处理步骤。

python
def filter_lines(file_path, keyword):
    """过滤包含特定关键字的行"""
    with open(file_path, 'r') as file:
        for line in file:
            if keyword in line:
                yield line.strip()

def transform_lines(lines, transformation):
    """对行进行转换"""
    for line in lines:
        yield transformation(line)

def write_results(results, output_file):
    """将结果写入文件"""
    with open(output_file, 'w') as file:
        for result in results:
            file.write(result + '\n')

# 构建数据处理管道
filtered_lines = filter_lines('input.txt', 'python')
transformed_lines = transform_lines(filtered_lines, lambda x: x.upper())
write_results(transformed_lines, 'output.txt')

4.2 协程应用案例

4.2.1 异步Web服务器

协程非常适合构建高性能的Web服务器,因为它可以同时处理多个请求,而不需要为每个请求创建一个新的线程或进程。

python
import asyncio
from aiohttp import web

async def handle(request):
    """处理HTTP请求"""
    name = request.match_info.get('name', "Anonymous")
    text = f"Hello, {name}!"
    return web.Response(text=text)

async def init_app():
    """初始化Web应用"""
    app = web.Application()
    app.router.add_get('/', handle)
    app.router.add_get('/{name}', handle)
    return app

# 运行Web服务器
web.run_app(init_app())

4.2.2 并发API请求

协程可以用来并发执行多个API请求,大大提高程序的执行效率。

python
import asyncio
import aiohttp
import time

async def fetch_data(session, url):
    """异步获取URL数据"""
    async with session.get(url) as response:
        return await response.json()

async def main():
    """主协程"""
    urls = [
        'https://jsonplaceholder.typicode.com/posts/1',
        'https://jsonplaceholder.typicode.com/posts/2',
        'https://jsonplaceholder.typicode.com/posts/3',
        'https://jsonplaceholder.typicode.com/posts/4',
        'https://jsonplaceholder.typicode.com/posts/5',
    ]
    
    start_time = time.time()
    
    async with aiohttp.ClientSession() as session:
        # 并发执行多个API请求
        tasks = [fetch_data(session, url) for url in urls]
        results = await asyncio.gather(*tasks)
        
        # 处理结果
        for i, result in enumerate(results):
            print(f"URL {i+1} 的标题: {result['title']}")
    
    end_time = time.time()
    print(f"总耗时: {end_time - start_time:.2f} 秒")

# 运行主协程
asyncio.run(main())

🎯 互动练习

练习1:实现一个无限序列生成器

任务:实现一个生成斐波那契数列的生成器函数fibonacci(),它可以生成无限长的斐波那契数列。

python
def fibonacci():
    """生成无限长的斐波那契数列"""
    # 你的代码这里

# 测试代码
fib = fibonacci()
for _ in range(10):  # 只获取前10个斐波那契数
    print(next(fib), end=' ')
# 预期输出: 0 1 1 2 3 5 8 13 21 34

练习2:实现异步定时器

任务:实现一个异步定时器函数async_timer(),它可以按照指定的时间间隔执行指定的回调函数。

python
import asyncio

async def async_timer(interval, callback, max_times=None):
    """异步定时器
    按照指定的时间间隔执行回调函数
    max_times: 最大执行次数,None表示无限执行
    """
    # 你的代码这里

# 测试代码
async def main():
    count = 0
    
    def on_timer():
        nonlocal count
        count += 1
        print(f"定时器触发第 {count} 次")
    
    # 每0.5秒执行一次on_timer,最多执行5次
    await async_timer(0.5, on_timer, 5)
    print("定时器测试完成")

asyncio.run(main())

练习3:实现异步数据处理管道

任务:使用协程实现一个简单的数据处理管道,包括:读取文件、处理数据、写入结果三个步骤。

python
import asyncio
import aiofiles

async def read_file(file_path):
    """异步读取文件"""
    # 你的代码这里

async def process_data(data):
    """异步处理数据"""
    # 你的代码这里(例如:计算单词频率)

async def write_results(results, output_file):
    """异步写入结果"""
    # 你的代码这里

async def main():
    """主协程"""
    data = await read_file('input.txt')
    results = await process_data(data)
    await write_results(results, 'output.txt')
    print("数据处理完成")

asyncio.run(main())

📝 小结

在本章中,我们深入探讨了Python中处理数据流和异步操作的强大工具:生成器、迭代器和协程。这些特性不仅可以使你的代码更高效、更优雅,还能帮助你处理大规模数据和构建高性能的异步应用。

主要内容回顾:

  1. 迭代器:是实现了__iter__()__next__()方法的对象,可以使用for循环遍历。
  2. 生成器:是一种特殊的迭代器,使用yield语句返回值,可以按需生成数据。
  3. 协程:是可以暂停和恢复执行的函数,使用async defawait语句定义和使用,是Python异步编程的基础。

这些特性在实际项目中有广泛的应用,特别是在处理大规模数据、构建高性能Web服务器、进行并发API请求等场景中。掌握这些特性,可以让你的Python编程技能更上一层楼。

在下一章节中,我们将学习Python中的元编程和反射,这是Python中最强大、最灵活的特性之一,也是高级Python编程的重要组成部分。

📚 推荐资源

  1. 官方文档

  2. 经典书籍

    • 《流畅的Python》 by Luciano Ramalho(第14章到第18章详细介绍了迭代器、生成器和协程)
    • 《Python Cookbook》 by David Beazley & Brian K. Jones(第4章包含了许多关于迭代器和生成器的实用技巧)
  3. 网络资源

© 2025 技术博客. All rights reserved by 老周有AI