Python中的yield与协程

1:yield函数

Python中的yield函数通常用于生成器generator当中,何为生成器?
在解释生成器概念前,先看一段代码观察yield函数的作用:

def fanhui(num):
    while num>0:
        print("当前num值:"+str(num))
        yield num
        num-=1
        print("现在值为:"+str(num))

num = 10
p = fanhui(num)

next(p)
print('========================================')
next(p)

其运行结果如下:

当前num值:10
========================================
现在值为:9
当前num值:9
9

可以看到,我们第一次调用next函数的时候,yield后面的程序没有运行,只运行了yield前面的程序,程序在yield函数运行后中断。
在第二次调用next函数的时候,程序从断点(yield处)进行运行,然后运行到下一个yield处再次停止。也就是说,当我们每调用一次next的时候,函数中的fanhui运行从yield处运行一个循环到下一个yield处中断,且记录下当前中断位置便于下一次程序开始。
看完这个例子,相信读者已经对yield有一个初步的了解了。我们再来看下面这个例子:

def L(num):
    while num>0:
        yield num
        num-=1

for i in L(10):
    print(i)

在这里插入图片描述
相信读者已经能够猜出相应的运行结果了,当我们用for 循环调用L()并传入初值num=10的时候,迭代器进入运行状态,然后在yield处返回值num给i,此时我们打印i的值。截止继续调用L()迭代器,L从上次中断的位置处继续运行,返回值9 ······

其实仔细分析一下不难看出,这不就是range(10)披了层马甲吗?对的,其实range()本质上就是一个迭代器,内部使用yield返回值的。这么做的原因是因为迭代器可以节约资源,没有必要一下子生成10个数,只需要每次调用的时候返回当前值就可以了。

可能你又要问了,for循环为啥可以调用L()/range()函数,实际上for循环进行迭代时,生成器在客户端发出请求前,不会产出任何值。在for循环中,Python隐式地在从生成器对象中获取的迭代器中调用next()。也就是说,在for循环中,Python隐式地执行以下操作:

next(L)
next(L)
next(L)
...

生成器大部分是使用yield函数的实现的,当我们调用一次生成器的时候,生成器运行到yield处中断,返回值。下一次调用就从该yield处开始,运行到下一个yield处再次中断,这就是生成器的原理。

2:协程

协程 ,又称为微线程,它是实现多任务的另一种方式,只不过是比线程更小的执行单元。因为它自带CPU的上下文,这样只要在合适的时机,我们可以把一个协程切换到另一个协程。
这里有一个通俗的理解

通俗的理解:
在一个线程中的某个函数中,我们可以在任何地方保存当前函数的一些临时变量等信息,然后切换到另外一个函数中执行,注意不是通过调用函数的方式做到的
,并且切换的次数以及什么时候再切换到原来的函数都由开发者自己确定。

一个进程中可以有多个线程,一个线程中可以由多个协程,而进程和线程的切换都是在系统层面上进行的,开销较大(当然进程的切换开销更大)。而协程的切换只是单纯的操作CPU的上下文,用户可以通过yield函数方便的从一个函数切换到另外一个函数当中。

import time

def task_1():
    while True:
        print("调用协程1,我在前面!")
        yield
        print("调用协程1,我在后面!")
        time.sleep(0.5)

def task_2():
    while True:
        print("调用协程2,我在前面!")
        yield
        print("调用协程2,我在后面!")
        time.sleep(0.5)
        
if __name__ == "__main__":
    t1 = task_1()  # 生成器对象
    t2 = task_2()
    # print(t1, t2)
    for _ in range(5):
        next(t1)  
        print("\n主线程!\n")  # 2、继续往下执行
        next(t2)  

其运行结果如下:

调用协程1,我在前面!

主线程!

调用协程2,我在前面!
调用协程1,我在后面!
调用协程1,我在前面!

主线程!

调用协程2,我在后面!
调用协程2,我在前面!
调用协程1,我在后面!
调用协程1,我在前面!

主线程!

调用协程2,我在后面!
调用协程2,我在前面!
调用协程1,我在后面!
调用协程1,我在前面!
...

从这个例子可以看出,使用yield可以很方便的完成协程间的切换。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值