Python学不会来打我(37)yield关键字详解,一篇讲清所有知识点

liftword10小时前技术文章3

在Python中,yield 关键字 是生成器(Generator)的核心组成部分。它使得函数可以“暂停执行”,并在后续调用时“继续执行”,从而实现了一种轻量级的协程机制。

本文将详细讲解 yield 的功能、使用方法、常见场景,并通过大量示例帮助你全面掌握这一重要概念。无论你是刚入门的新手,还是想深入理解生成器原理的开发者,这篇文章都能为你提供清晰的知识体系和实践指导。


一、什么是 yield?

yield 是一个关键字,用于定义生成器函数。当一个函数中包含 yield 表达式时,该函数就不再是普通的函数,而是一个生成器函数。

普通函数 vs 生成器函数

二、yield的基本使用方法

示例1:最简单的生成器函数

def simple_generator():
    yield "Hello"
    yield "World"
    yield "!"

# 调用生成器函数
gen = simple_generator()

print(next(gen))  # 输出 Hello
print(next(gen))  # 输出 World
print(next(gen))  # 输出 !
print(next(gen))  # 抛出 StopIteration 异常

注意:生成器函数不会立即执行其代码,而是返回一个生成器对象。只有在调用 next() 或使用 for 循环时才会逐步执行。


示例2:带循环的生成器函数

def count_up_to(n):
    i = 1
    while i <= n:
        yield i
        i += 1

# 使用 for 循环遍历生成器
for num in count_up_to(5):
    print(num)

输出:

1
2
3
4
5

在这个例子中,count_up_to 函数会逐个返回数字,而不是一次性把列表全部构建出来,节省了内存空间。


三、yield的工作机制

生成器之所以能实现“按需生成”,是因为它内部维护了一个状态机。每当遇到 yield 语句时,函数会暂停执行,并保存当前的状态;下次调用 next() 时,函数从中断处继续执行。

示例3:yield与状态保持

def stateful_generator():
    value = 0
    while True:
        value += 1
        yield value

gen = stateful_generator()
print(next(gen))  # 输出 1
print(next(gen))  # 输出 2
print(next(gen))  # 输出 3

每次调用 next(gen),函数都会从上次 yield 的位置继续执行,保留了 value 的状态。


四、yield的高级用法

除了基本的 yield 表达式,我们还可以利用以下特性来增强生成器的功能:

1. yield表达式赋值 —— 实现双向通信

def echo():
    while True:
        received = yield
        print("收到:", received)

gen = echo()
next(gen)  # 启动生成器
gen.send("你好")  # 发送数据
gen.send("再见")

输出:

收到: 你好
收到: 再见

注意:第一次必须调用 next() 或 send(None) 来启动生成器。


2. throw() 方法 —— 在生成器中抛出异常

def my_gen():
    try:
        yield 1
        yield 2
    except ValueError:
        print("捕获到异常")

gen = my_gen()
print(next(gen))  # 输出 1
gen.throw(ValueError)  # 触发异常

输出:

1
捕获到异常

这个功能可用于在生成器中处理错误或中断流程。


3. close() 方法 —— 关闭生成器

def infinite_gen():
    while True:
        yield "Hello"

gen = infinite_gen()
print(next(gen))  # Hello
gen.close()       # 终止生成器

调用 close() 会触发 GeneratorExit 异常,确保生成器释放资源。


五、yield的应用场景

场景1:惰性求值与大数据处理

当我们需要处理非常大的数据集时,一次性加载所有数据会占用大量内存。使用 yield 可以实现按需生成,节省内存。

def read_large_file(file_path):
    with open(file_path, 'r', encoding='utf-8') as f:
        for line in f:
            yield line.strip()

for line in read_large_file('big_data.txt'):
    print(line)

场景2:无限序列生成器

比如斐波那契数列、质数生成等。

def fibonacci():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

fib = fibonacci()
for _ in range(10):
    print(next(fib))

输出前10个斐波那契数:

0
1
1
2
3
5
8
13
21
34

场景3:协程(Coroutine)与异步编程

生成器可以作为协程使用,在异步编程中有广泛应用。

def consumer():
    while True:
        item = yield
        print("消费了:", item)

def producer(consumer_gen):
    for i in range(5):
        print("生产了:", i)
        consumer_gen.send(i)

prod = producer(consumer())
next(prod)  # 启动协程

场景4:懒加载与资源控制

例如数据库查询结果分页获取。

def query_database(limit=10):
    offset = 0
    while True:
        results = fetch_from_db(offset, limit)
        if not results:
            break
        for row in results:
            yield row
        offset += limit

六、yield的优点与注意事项

优点:

  • 节省内存:不一次性加载所有数据
  • 支持惰性求值:只在需要时计算
  • 状态自动保存:无需手动管理变量状态
  • 可组合性强:多个生成器可以串联使用,构建复杂流程

注意事项:

  • 不可重复使用:一旦遍历完成就“耗尽”
  • 不能索引访问:不像列表那样可以直接通过下标取值
  • 学习曲线略陡:特别是涉及 send / throw / close 时
  • 调试困难:由于函数是分段执行的,调试时不易追踪流程

七、yield与return的区别

八、yield与生成器表达式的区别

除了生成器函数,我们还可以使用生成器表达式创建简洁的生成器。

示例:生成器表达式 vs 列表推导式

# 列表推导式:一次性生成所有元素
squares_list = [x*x for x in range(10)]
print(squares_list)  # 输出完整列表

# 生成器表达式:按需生成
squares_gen = (x*x for x in range(10))
print(squares_gen)   # <generator object ...>

两者区别在于:

  • 列表推导式一次性生成整个列表,占用内存
  • 生成器表达式只在需要时计算下一个值,节省内存

九、总结

yield 是 Python 中实现生成器和协程的关键字。它让我们能够轻松地编写出高效、灵活的数据处理逻辑,尤其适合处理大数据、流式数据、异步任务等场景。

通过本文的学习,你应该已经掌握了:

  • yield 的基本语法和工作原理
  • 如何定义生成器函数并使用 next() 和 for 遍历
  • yield 的高级用法(send、throw、close)
  • 常见应用场景(大文件处理、无限序列、协程等)
  • yield 与 return、生成器表达式的异同点

作为 Python 初学者,建议你在练习中多尝试使用 yield 来优化代码结构,提升性能,并结合实际需求进行拓展。

希望这篇文章能帮助你在 Python 编程之路上越走越远!

相关文章

每天一个Python库:lxml全面实战指南,爬虫解析速度翻倍

你是否遇到过:BeautifulSoup 解析太慢?re 提取数据太容易误匹配?抓数据时 HTML 太乱根本不好搞?本期,我们用 lxml + XPath 一把梭!学习本来就不是一蹴而就的事,不过只要...

Python中subprocess模块:轻松调用外部程序与命令

大家好!在Python的世界里,subprocess模块提供了强大且灵活的方式来创建和管理子进程,让Python 程序能够调用外部程序、执行系统命令。一、subprocess模块简介subproces...

python入门到脱坑—字符串的切片

在 Python 中,字符串切片(String Slicing)是一种强大的操作,可以灵活地提取子字符串。以下是 字符串切片的详细指南,包含基础语法、高级技巧和实际应用场景:1. 基础切片语法text...

Python学不会来打我(81)yield关键字的作用总结

上一篇文章我们介绍了yield创建的生成器,yield除了创建生成器之外,还有其他的作用,今天我们就分享yield关键字的其他几个作用!#python##python教程##python自学##...

Python 中的前缀删除操作全指南

1. 字符串前缀删除1.1 使用内置方法Python 提供了几种内置方法来处理字符串前缀的删除:# 1. 使用 removeprefix() 方法 (Python 3.9+) text = "...