全网都在找的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-第一篇+源码讲解

相关推荐
开心工作室_kaic15 分钟前
ssm161基于web的资源共享平台的共享与开发+jsp(论文+源码)_kaic
java·开发语言·前端
懒洋洋大魔王16 分钟前
RocketMQ的使⽤
java·rocketmq·java-rocketmq
武子康21 分钟前
Java-06 深入浅出 MyBatis - 一对一模型 SqlMapConfig 与 Mapper 详细讲解测试
java·开发语言·数据仓库·sql·mybatis·springboot·springcloud
转世成为计算机大神1 小时前
易考八股文之Java中的设计模式?
java·开发语言·设计模式
小喵要摸鱼1 小时前
Python 神经网络项目常用语法
python
qq_327342731 小时前
Java实现离线身份证号码OCR识别
java·开发语言
长亭外的少年2 小时前
Kotlin 编译失败问题及解决方案:从守护进程到 Gradle 配置
android·开发语言·kotlin
阿龟在奔跑2 小时前
引用类型的局部变量线程安全问题分析——以多线程对方法局部变量List类型对象实例的add、remove操作为例
java·jvm·安全·list
飞滕人生TYF3 小时前
m个数 生成n个数的所有组合 详解
java·递归
一念之坤3 小时前
零基础学Python之数据结构 -- 01篇
数据结构·python