异步编程的基础在于理解协程,而协程的基础在于理解生成器,而生成器的基础在于理解yield关键字,下面就来说说这几个概念。
什么是yield关键字?
相当于return关键字,在每次next(),或者for遍历的时候,都会yield这里将新的值返回回去,并在这里阻塞,等待下一次的调用
什么是生成器?
每次触发只会返回一个值,用next(generator) 和generator.send() 触发,比如下面这个例子。
from collections.abc import Generator
def target(N):
count = 0
while count < N:
yield count
count += 1
tar = target(5)
print(isinstance(tar, Generator)) # True
print(next(tar)) # 0, 因为一开始count=0, 执行到yield count就把count返回,所以会打印 0
print(tar.send(None)) # 1, 执行完该语句后,会跳转到yield count;并继续执行count+=1, 所以count就变成1;再执行yield count, 将1返回。
生成器的注意事项?
- 注意生成器的实例个数
- 注意send()方法传递的参数
- 当参数为None时, 与next(generator)等价
- 当参数为数字时,会通过yield关键字传递到生成器
talk is cheap,show me the code!
# 第一个注意事项,生成器的实例个数(与之前的代码进行比较)
from collections.abc import Generator
def target(N):
count = 0
while count < N:
yield count
count += 1
print(isinstance(target(5), Generator)) # True
print(next(target(5))) # 0
print(target(5).send(None)) # 0,这里返回0是相当于创建一个新的生成器实例,所以第一个值返回的肯定是0
# 第二个注意事项,send方法传递的参数
# 1.jump = yield index 应该拆分为两个语句,分别是 jump = yield 和 yield(return) index;
# 2.jump = yield接收通过send()发送的信息,并赋值给jump;yield index 是将index return给外部程序。
def jumping_range(N):
index = 0
while index < N:
jump = yield index
if jump is None:
jump = 1
index += jump
if __name__ == '__main__':
itr = jumping_range(5)
print(itr.send(None)) # 1
print(next(itr)) # 2
print(itr.send(2)) # 3, 这条语句不能放到位置1, 具体可以自己尝试
想必到这里大家应该对生成器和yield有个了解,那什么是协程呢?
协程:
网上常见说法是轻量线程,它的关系类似与线程与进程的关系,但是它不用考虑线程安全。
我的理解是协程是程序之间协作配合,或者说是不同函数之间的配合,因为它的级别只是函数,所以它在单个线程内,也就不同考虑线程安全。而且如果我们把每个函数抽象为一个任务,其实协程实现了单线程多任务,这也是它的一个亮点之一,提升了效率。
这段话有两个比较难理解的,一是函数怎么配合?二是效率怎么提升?
# 在这段程序中,await asyncio.sleep(x) 会阻塞函数相应的秒数,如果按照同步的方式,总共应该是6秒,而通过协程,其实实现了异步,时间变为4秒!
import asyncio
import time
start = time.time()
# 协程函数
async def do_some_work(x):
print('Waiting: ', x)
await asyncio.sleep(x)
return 'Done after {}s'.format(x)
# 协程对象
coroutine1 = do_some_work(2)
coroutine3 = do_some_work(4)
# 将协程转成task,并组成list
tasks = [
asyncio.ensure_future(coroutine1),
asyncio.ensure_future(coroutine3)
]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
for task in tasks:
print('Task ret: ', task.result())
print("time_consume:", time.time() - start)
----------
Waiting: 2
Waiting: 4
Task ret: Done after 2s
Task ret: Done after 4s
time_consume: 4.002617359161377
「Python异步编程-先吃个面包」就写到这里,希望你们读了之后能有面包那种饱腹感我就心满意足了。
其实关于yield,生成器,协程有很多概念没将明白,只是抽出了自己觉得比较重要的,更加全面的教程可以看这里(良心推荐):
Python进阶开发之路
另外,廖雪峰大大的教程也可以作为辅助性教程
「Python异步编程-1」总览全局
「Python异步编程-2」可迭代对象、迭代器、生成器
「Python异步编程-3」协程、生成器、yield 的联系
「Python异步编程-4」-协程的工作流程
网友评论