全网都在找的Python生成器竟然在这里!简单几步,让你的代码更简洁、更高效!

  • 博客主页:长风清留扬-CSDN博客
  • 系列专栏:Python基础专栏
  • 每天更新大数据相关方面的技术,分享自己的实战工作经验和学习总结,尽量帮助大家解决更多问题和学习更多新知识,欢迎评论区分享自己的看法
  • 感谢大家点赞👍收藏⭐评论

一、生成器的定义

生成器是一种特殊的迭代器,它允许你定义一个函数,该函数会按照你的要求生成一个序列的值,但一次只返回一个值,并且在内部维护着自己的状态,以便在需要时生成下一个值。生成器是通过使用yield关键字来实现的。
推荐阅读: 来看看Python迭代器能让你的代码提升100倍的密码

二、生成器的创建

生成器可以通过以下两种方式创建:

yield关键字

函数中包含yield关键字:当一个函数中包含至少一个yield语句时,该函数就不再是一个普通函数,而是一个生成器函数。调用该函数时,会返回一个生成器对象。

在Python中,yield关键字用于在函数中创建一个生成器。生成器是一种特殊的迭代器,它允许你逐个产生值,而不是一次性返回整个序列。使用yield关键字,你可以定义一个函数,该函数在每次调用时返回一个值,并保留其状态以供下次调用时继续执行。

语法

python 复制代码
def generator_function_name(parameters):  
    """  
    这是一个生成器函数的定义。  
      
    :param parameters: 函数的参数,可以是任意数量和类型的参数。  
    :yield: 在函数体中,使用yield关键字来返回一个值,并暂停函数的执行。  
            每次迭代生成器时,函数会从上次yield的位置继续执行。  
    """  
    # 函数体:可以包含任意数量的语句和逻辑。  
    # 通常会有一个循环来逐个产生值。  
    for item in iterable:  # iterable是一个可迭代对象,比如列表、元组、集合或字符串等。  
        # 可以在这里进行任何需要的计算或处理。  
        yield value  # 使用yield返回一个值,并暂停函数的执行。  
        # 这里的代码(如果有的话)不会在每次迭代时都执行,  
        # 除非它位于另一个循环或条件语句中。  
    # 注意:生成器函数通常没有return语句(或者有一个不带值的return,表示迭代结束)。  
    # 如果有return语句并带有值,那么在迭代结束后尝试获取下一个值时,会引发StopIteration异常,  
    # 并且该值会被作为StopIteration异常的value属性返回(这是Python 3.3及以后版本的行为)。

示例1

python 复制代码
def simple_generator():  
    """  
    一个简单的生成器函数,依次返回数字0到2。  
    """  
    yield 0  
    yield 1  
    yield 2  
  
# 使用生成器函数  
gen = simple_generator()  # 创建一个生成器对象  
  
# 迭代生成器对象  
for value in gen:  
    print(value)  # 输出:0, 1, 2

在上面的示例中,simple_generator是一个生成器函数,它使用yield关键字依次返回数字0、1和2。当我们调用这个函数时,它不会立即执行完并返回结果,而是返回一个生成器对象。然后,我们可以使用for循环或其他迭代方式来逐个获取生成器产生的值。

需要注意的是,生成器函数在每次迭代时都会从上次yield的位置继续执行,直到函数结束或再次遇到yield。这意味着你可以在生成器函数中保留状态(比如循环变量、内部变量等),并在下次迭代时继续使用它们。

示例2

python 复制代码
def fibonacci(n):  
    """生成斐波那契数列的前n个数"""  
    a, b = 0, 1  
    for _ in range(n):  
        yield a  # 返回当前的斐波那契数  
        a, b = b, a + b  # 更新斐波那契数列的当前数和下一个数  
  
# 使用生成器函数  
fib_gen = fibonacci(5)  # 创建一个生成器对象  
for num in fib_gen:  # 迭代生成器对象  
    print(num)  # 打印每个斐波那契数

运行结果:

python 复制代码
0  
1  
1  
2  
3

生成器表达式

  • 生成器表达式:生成器表达式类似于列表推导式,但使用圆括号而不是方括号。它们提供了一种简洁的方法来创建生成器。
  • 生成器表达式是另一种创建生成器的方式,它提供了一种简洁的语法来生成序列的值。生成器表达式使用圆括号()而不是方括号[]来定义,并且它们也使用yield关键字(尽管在表达式中这种用法是隐式的)。

语法

python 复制代码
generator_expression = (expression for item in iterable [if condition])
  • expression:要生成的值的表达式。
  • item:从可迭代对象iterable中取出的元素。
  • iterable:要遍历的可迭代对象。
  • if condition(可选):一个条件表达式,用于过滤要生成的元素。

示例

python 复制代码
# 创建一个生成器表达式,生成从0到9的平方数  
squares = (x**2 for x in range(10))  
  
# 使用for循环迭代生成器表达式  
for square in squares:  
    print(square)

运行结果:

python 复制代码
0  
1  
4  
9  
16  
25  
36  
49  
64  
81

生成器表达式在语法上类似于列表推导式,但它们不会一次性生成整个列表,而是返回一个生成器对象,该对象在迭代时按需生成值。这使得生成器表达式在处理大数据集时更加高效,因为它们不会占用大量内存。

总之,生成器是Python中一个非常有用的特性,它们提供了一种惰性计算的方式,只在需要时才生成值,从而节省了内存和计算资源。通过生成器函数和生成器表达式,我们可以轻松地创建和使用生成器来处理迭代逻辑和大数据集。

三、访问生成器

使用for循环遍历生成器

首先,我们定义一个简单的生成器函数:

python 复制代码
# 定义一个生成器函数,它逐个生成从0到4的数字  
def simple_generator():  
    for i in range(5):  
        yield i  # 使用yield关键字,使函数成为一个生成器  
  
# 使用for循环遍历生成器  
for value in simple_generator():  
    print(f"For loop value: {value}")

运行结果

python 复制代码
For loop value: 0  
For loop value: 1  
For loop value: 2  
For loop value: 3  
For loop value: 4

使用__next__()方法访问生成器

生成器对象本身也有__next__()方法,可以实现与next()函数相同的功能:

python 复制代码
# 重新创建生成器对象  
gen = simple_generator()  
  
# 使用生成器对象的__next__()方法获取下一个值  
print(f"__next__() value: {gen.__next__()}")  # 输出第一个值  
print(f"__next__() value: {gen.__next__()}")  # 输出第二个值  
print(f"__next__() value: {gen.__next__()}")  # 输出第三个值  
  
# 当生成器耗尽时,继续调用__next__()会引发StopIteration异常  
try:  
    print(f"__next__() value: {gen.__next__()}")  # 输出第四个值  
    print(f"__next__() value: {gen.__next__()}")  # 尝试获取第五个值,会抛出异常  
except StopIteration:  
    print("Generator exhausted")

输出结果:

python 复制代码
__next__() value: 0  
__next__() value: 1  
__next__() value: 2  
__next__() value: 3  
Generator exhausted

使用next()方法访问生成器

我们可以直接使用next()函数来获取生成器的下一个值:

python 复制代码
# 重新创建生成器对象  
gen = simple_generator()  
  
# 使用next()方法获取生成器的下一个值  
print(f"Next value: {next(gen)}")  # 输出第一个值  
print(f"Next value: {next(gen)}")  # 输出第二个值  
print(f"Next value: {next(gen)}")  # 输出第三个值  
  
# 当生成器耗尽时,继续调用next()会引发StopIteration异常  
try:  
    print(f"Next value: {next(gen)}")  # 输出第四个值  
    print(f"Next value: {next(gen)}")  # 尝试获取第五个值,会抛出异常  
except StopIteration:  
    print("Generator exhausted")

运行结果:

python 复制代码
Next value: 0  
Next value: 1  
Next value: 2  
Next value: 3  
Generator exhausted

使用send()方法

send()方法不仅可以获取生成器的下一个值,还可以向生成器发送一个值(通过yield表达式接收)。需要注意的是,首次启动生成器不能使用send(),而必须使用next()或__next__()。

python 复制代码
# 定义一个接受send值的生成器函数  
def generator_with_send():  
    received = yield "Start"  # 第一个yield,此时received是None  
    while True:  
        received = yield received  # 后续yield,将接收到的值发送回去  
  
# 创建生成器对象  
gen = generator_with_send()  
  
# 启动生成器  
print(f"First value: {next(gen)}")  # 必须首先使用next()启动  
  
# 使用send()发送值并获取生成器的下一个值  
print(f"Send value: {gen.send('First send value')}")  # 发送值并获取"First send value"  
print(f"Send value: {gen.send('Second send value')}")  # 再次发送值并获取"Second send value"  
  
# 为了避免无限循环,我们在这里停止  
# 在实际应用中,可以根据条件在生成器内部控制循环的结束

输出结果:

python 复制代码
# 定义一个接受send值的生成器函数  
def generator_with_send():  
    received = yield "Start"  # 第一个yield,此时received是None  
    while True:  
        received = yield received  # 后续yield,将接收到的值发送回去  
  
# 创建生成器对象  
gen = generator_with_send()  
  
# 启动生成器  
print(f"First value: {next(gen)}")  # 必须首先使用next()启动  
  
# 使用send()发送值并获取生成器的下一个值  
print(f"Send value: {gen.send('First send value')}")  # 发送值并获取"First send value"  
print(f"Send value: {gen.send('Second send value')}")  # 再次发送值并获取"Second send value"  
  
# 为了避免无限循环,我们在这里停止  
# 在实际应用中,可以根据条件在生成器内部控制循环的结束

输出结果:

python 复制代码
First value: Start  
Send value: First send value  
Send value: Second send value

四、yield关键字的作用

yield关键字在生成器函数中有以下几个作用:

  • 返回一个值给调用者:每次调用next()方法或迭代生成器对象时,生成器函数会从上次离开的位置继续执行,直到遇到下一个yield语句,并返回该语句的值。
  • 暂停函数执行:当生成器函数执行到yield语句时,它会暂停执行,并保存当前的所有局部变量和状态。下次调用next()方法时,它会从上次暂停的位置继续执行。
  • 记忆状态:生成器能够记住上一次迭代时的状态,这使得它能够在多次迭代中保持内部状态的一致性。

五、生成器的优势

生成器具有以下几个优势:

  • 惰性计算:生成器只在需要时才计算下一个值,这可以节省内存和计算资源。
  • 节省内存:由于生成器一次只返回一个值,并且使用迭代器协议进行迭代,因此它们可以处理大数据集而不会耗尽内存。
  • 简化代码:生成器提供了一种简洁的方法来编写迭代逻辑,使得代码更加清晰和易于维护。

六、生成器的应用场景

生成器适用于以下场景:

  • 处理大数据集:当数据集非常大时,使用生成器可以避免一次性将所有数据加载到内存中。
  • 需要惰性计算的场景:当只需要处理数据集的一部分时,生成器可以按需生成值,而无需计算整个数据集。
  • 自定义迭代逻辑:当需要自定义迭代逻辑时,生成器提供了一种灵活的方式来实现这一点。

推荐阅读

Python基础

Python全网最全基础课程笔记(一)------基础入门
Python全网最全基础课程笔记(二)------变量
Python全网最全基础课程笔记(三)------所有运算符+运算符优先级
Python全网最全基础课程笔记(四)------基本数据类型
Python全网最全基础课程笔记(五)------选择结构+Python新特性Match
Python全网最全基础课程笔记(六)------循环结构
Python全网最全基础课程笔记(七)------列表,跟着思维导图和图文来学习,爆肝2w字,无数代码案例!
Python全网最全基础课程笔记(八)------字典,跟着思维导图和图文来学习,爆肝2w字,无数代码案例!
Python全网最全基础课程笔记(九)------集合,跟着思维导图和图文来学习,爆肝2w字,无数代码案例!
Python全网最全基础课程笔记(十)------元组,跟着思维导图和图文来学习,爆肝2w字,无数代码案例!
Python全网最全基础课程笔记(十一)------字符串所有操作,跟着思维导图和图文来学习,爆肝2w字,无数代码案例!
Python全网最全基础课程笔记(十二)------函数,跟着思维导图和图文来学习,爆肝2w字,无数代码案例!
Python全网最全基础课程笔记(十三)------作用域,跟着思维导图和图文来学习,爆肝2w字,无数代码案例!

Flink入门到就业

2024年最新Flink教程,从基础到就业,大家一起学习--基础篇
2024年最新Flink教程,从基础到就业,大家一起学习--入门篇
2024年最新Flink教程,从基础到就业,大家一起学习--Flink集群部署
2024年最新Flink教程,从基础到就业,大家一起学习--flink部署和集群部署(从本地测试到公司生产环境如何部署项目源码)
2024年最新Flink教程,从基础到就业,大家一起学习--Flink运行架构底层源码详解+实战
2024年最新Flink教程,从基础到就业,大家一起学习--Flink DataStream API-第一篇+源码讲解

相关推荐
小羊在奋斗13 分钟前
【C++】二叉搜索树+变身 = AVL树
java·开发语言·c++·机器学习
B站计算机毕业设计超人14 分钟前
计算机毕业设计Django+Vue.js豆瓣图书推荐系统 图书评论情感分析 豆瓣图书可视化大屏 豆瓣图书爬虫 数据分析 图书大数据 大数据毕业设计 机器学习
大数据·python·深度学习·机器学习·网络爬虫·数据可视化·推荐算法
疯一样的码农1 小时前
Spring Boot Starter Parent介绍
java·spring boot·后端
iQM751 小时前
Spring Boot集成RBloomFilter快速入门Demo
java·spring boot·spring
爱上语文1 小时前
Springboot 阿里云对象存储OSS 工具类
java·开发语言·spring boot·后端·阿里云
某风吾起1 小时前
阿里云物联网自有app创建之初始化SDK
android
代码代码快快显灵4 小时前
java之异常处理
java·开发语言
阳光男孩014 小时前
MySQL基础之约束
android·数据库·mysql
茶馆大橘4 小时前
Spring Validation —— 参数校验框架
java·后端·学习·spring
我的运维人生5 小时前
Android架构组件MVVM模式的实战应用与数据绑定技巧
android·架构·运维开发·技术共享