分享

Python中的yield用法比想象的强大

 喜欢站在山上 2024-01-17 发布于吉林
Python中的yield用法比想象的强大

Python中的yield用法是一个比较高级的话题,它可以让一个函数变成一个生成器,也就是一个可以按需产生值的可迭代对象。使用yield的函数不再是一个普通的函数,而是在每次调用时返回一个新的值,同时保留上一次的状态。这样可以实现一些高效的功能,比如惰性求值、协程、并发等。

一个最简单的例子,使用yield生成一个斐波那契数列:

# 定义一个生成器函数def fib(n): a, b = 0, 1 # 初始化两个变量 while a < n: # 循环条件 yield a # 返回当前值,并暂停执行 a, b = b, a + b # 更新两个变量# 调用生成器函数,返回一个生成器对象g = fib(10)print(g) # <generator object fib at 0x0000020C2E4E5F90># 使用for循环遍历生成器对象for x in g: print(x) # 依次输出0, 1, 1, 2, 3, 5, 8# 使用next()函数获取生成器对象的下一个值g = fib(10) # 重新创建一个生成器对象print(next(g)) # 0print(next(g)) # 1print(next(g)) # 1

从上面的例子可以看出,使用yield的函数在每次调用时都会返回一个新的值,并且记住上一次的状态。这样就可以避免使用列表或其他数据结构来存储中间结果,节省了内存空间。而且,生成器对象是惰性求值的,也就是说只有在需要时才会计算下一个值,提高了执行效率。

其次,来看一下如何使用生成器对象的其他方法,比如send()和throw()。这两个方法可以从外部向生成器内部传递数据或异常,从而改变生成器的行为。例如:

# 定义一个生成器函数def gen():    i = None # 初始化一个变量    while True: # 无限循环        j = yield i # 返回当前值,并接收外部传入的值        print('Received:', j) # 打印接收到的值        if j == -1: # 如果接收到-1,就终止循环            break        i = j * 2 # 更新当前值为接收到的值乘以2# 创建一个生成器对象g = gen()print(next(g)) # None# 使用send()方法向生成器内部传递数据,并获取下一个值print(g.send(1)) # Received: 1; 2print(g.send(2)) # Received: 2; 4# 使用throw()方法向生成器内部抛出异常,并获取下一个值try:    print(g.throw(ValueError)) # Received: None; ValueError: except ValueError:    print('ValueError raised')# 使用close()方法关闭生成器,并释放资源g.close()

从上面的例子可以看出,使用send()方法可以向生成器内部传递数据,并赋值给yield前面的变量。这样就可以实现一种双向通信,让外部和生成器之间互相影响。使用throw()方法可以向生成器内部抛出异常,并触发相应的处理逻辑。使用close()方法可以关闭生成器,并释放相关资源。

最后,来看一下如何使用yield实现协程和并发。协程是一种轻量级的线程,它可以在多个任务之间切换执行,而不需要操作系统的干预。并发是一种编程模式,它可以让多个任务同时进行,提高程序的运行效率。例如:

import time# 定义一个协程函数def task(name): print(f'{name} start') yield # 暂停执行,等待切换 print(f'{name} resume') yield # 暂停执行,等待切换 print(f'{name} end')# 创建两个协程对象t1 = task('Task1')t2 = task('Task2')# 依次执行两个协程对象的第一部分next(t1) # Task1 startnext(t2) # Task2 start# 依次执行两个协程对象的第二部分next(t1) # Task1 resumenext(t2) # Task2 resume# 依次执行两个协程对象的第三部分next(t1) # Task1 endnext(t2) # Task2 end# 定义一个并发函数def concurrent_task(name, n): for i in range(n): print(f'{name}: {i}') yield # 暂停执行,等待切换# 创建两个并发任务c1 = concurrent_task('TaskA', 5)c2 = concurrent_task('TaskB', 5)# 使用循环交替执行两个并发任务,直到完成while True: try: next(c1) next(c2) time.sleep(0.5) # 模拟延迟 except StopIteration: break# Output:# TaskA: 0# TaskB: 0# TaskA: 1# TaskB: 1# TaskA: 2# TaskB: 2# TaskA: 3# TaskB: 3# TaskA: 4# TaskB: 4

从上面的例子可以看出,使用yield可以实现协程和并发的功能,让多个任务可以在同一个线程中交替执行,提高程序的响应速度和资源利用率。

总之,Python中的yield用法是一个非常强大和灵活的特性,它可以让一个函数变成一个生成器,也就是一个可以按需产生值的可迭代对象。使用yield的函数不再是一个普通的函数,而是在每次调用时返回一个新的值,同时保留上一次的状态。这样可以实现一些高效的功能,比如惰性求值、协程、并发等。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多