关于我

一个有思想的程序猿,终身学习实践者,目前在一个创业团队任team lead,技术栈涉及Android、Python、Java和Go,这个也是我们团队的主要技术栈。

Github:https://github.com/hylinux1024

微信公众号:终身开发者(angrycode)

列表生成器

使用yield定义的函数

在Python早期的版本中协程也是通过生成器来实现的,也就是基于生成器的协程(Generator-based Coroutines)。在前一篇介绍生成器的文章末尾举了一个生产者-消费者的例子,就是基于生成器的协程来实现的。

def producer(c):

n = 0

while n < 5:

n += 1

print('producer {}'.format(n))

r = c.send(n)

print('consumer return {}'.format(r))

def consumer():

r = ''

while True:

n = yield r

if not n:

return

print('consumer {} '.format(n))

r = 'ok'

if __name__ == '__main__':

c = consumer()

next(c) # 启动consumer

producer(c)

看了这段代码,相信很多初学者和我一样对基于生成器的协程实现其实很难马上就能够根据业务写出自己的协程代码。Python实现者们也注意到这个问题,因为它太不Pythonic了。而基于生成器的协程也将被废弃,因此本文将重点介绍asyncio包的使用,以及涉及到的一些相关类概念。

注:我使用的Python环境是3.7。

0x00 何为协程(Coroutine)

协程(Coroutine)是在线程中执行的,可理解为微线程,但协程的切换没有上下文的消耗,它比线程更加轻量些。一个协程可以随时中断自己让另一个协程开始执行,也可以从中断处恢复并继续执行,它们之间的调度是由程序员来控制的(可以看本文开篇处生产者-消费者的代码)。

定义一个协程

在Python3.5+版本新增了aysnc和await关键字,这两个语法糖让我们非常方便地定义和使用协程。

在函数定义时用async声明就定义了一个协程。

import asyncio

# 定义了一个简单的协程

async def simple_async():

print('hello')

await asyncio.sleep(1) # 休眠1秒

print('python')

# 使用asynio中run方法运行一个协程

asyncio.run(simple_async())

# 执行结果为

# hello

# python

在协程中如果要调用另一个协程就使用await。要注意await关键字要在async定义的函数中使用,而反过来async函数可以不出现await

# 定义了一个简单的协程

async def simple_async():

print('hello')

asyncio.run(simple_async())

# 执行结果

# hello

asyncio.run()将运行传入的协程,负责管理asyncio事件循环。

除了run()方法可直接执行协程外,还可以使用事件循环loop

async def do_something(index):

print(f'start {time.strftime("%X")}', index)

await asyncio.sleep(1)

print(f'finished at {time.strftime("%X")}', index)

def test_do_something():

# 生成器产生多个协程对象

task = [do_something(i) for i in range(5)]

# 获取一个事件循环对象

loop = asyncio.get_event_loop()

# 在事件循环中执行task列表

loop.run_until_complete(asyncio.wait(task))

loop.close()

test_do_something()

# 运行结果

# start 00:04:03 3

# start 00:04:03 4

# start 00:04:03 1

# start 00:04:03 2

# start 00:04:03 0

# finished at 00:04:04 3

# finished at 00:04:04 4

# finished at 00:04:04 1

# finished at 00:04:04 2

# finished at 00:04:04 0

可以看出几乎同时启动了所有的协程。

其实翻阅源码可知asyncio.run()的实现也是封装了loop对象及其调用。而asyncio.run()每次都会创建一个新的事件循环对象用于执行协程。

0x01 Awaitable对象

在Python中可等待(Awaitable)对象有:协程(corountine)、任务(Task)、Future。即这些对象可以使用await关键字进行调用

await awaitable_object

1. 协程(Coroutine)

协程由async def声明定义,一个协程可由另一个协程使用await进行调用

async def nested():

print('in nested func')

return 13

async def outer():

# 要使用await 关键字 才会执行一个协程函数返回的协程对象

print(await nested())

asyncio.run(outer())

# 执行结果

# in nested func

# 13

如果在outer()方法中直接调用nested()而不使用await,将抛出一个RuntimeWarning

async def outer():

# 直接调用协程函数不会发生执行,只是返回一个 coroutine 对象

nested()

asyncio.run(outer())

运行程序,控制台将输出以下信息

RuntimeWarning: coroutine 'nested' was never awaited

nested()

RuntimeWarning: Enable tracemalloc to get the object allocation traceback

2. 任务(Task)

任务(Task)是可以用来并发地执行协程。可以使用asyncio.create_task()将一个协程对象封装成任务,该任务将很快被排入调度队列并执行。

async def nested():

print('in nested func')

return 13

async def create_task():

# create_task 将一个协程对象打包成一个 任务时,该协程就会被自动调度运行

task = asyncio.create_task(nested())

# 如果要看到task的执行结果

# 可以使用await等待协程执行完成,并返回结果

ret = await task

print(f'nested return {ret}')

asyncio.run(create_task())

# 运行结果

# in nested func

# nested return 13

注:关于并发下文还会详细说明。

3. Future

Future是一种特殊的低层级(low-level)对象,它是异步操作的最终结果(eventual result)。

当一个 Future 对象 被等待,这意味着协程将保持等待直到该 Future 对象在其他地方操作完毕。

通常在应用层代码不会直接创建Future对象。在某些库和asyncio模块中的会使用到该对象。

async def used_future_func():

await function_that_returns_a_future_object()

0x02 并发

1. Task

前面我们知道Task可以并发地执行。 asyncio.create_task()就是一个把协程封装成Task的方法。

async def do_after(what, delay):

await asyncio.sleep(delay)

print(what)

# 利用asyncio.create_task创建并行任务

async def corun():

task1 = asyncio.create_task(do_after('hello', 1)) # 模拟执行1秒的任务

task2 = asyncio.create_task(do_after('python', 2)) # 模拟执行2秒的任务

print(f'started at {time.strftime("%X")}')

# 等待两个任务都完成,两个任务是并行的,所以总时间两个任务中最大的执行时间

await task1

await task2

print(f'finished at {time.strftime("%X")}')

asyncio.run(corun())

# 运行结果

# started at 23:41:08

# hello

# python

# finished at 23:41:10

task1是一个执行1秒的任务,task2是一个执行2秒的任务,两个任务并发的执行,总共消耗2秒。

2. gather

除了使用asyncio.create_task()外还可以使用asyncio.gather(),这个方法接收协程参数列表

async def do_after(what, delay):

await asyncio.sleep(delay)

print(what)

async def gather():

print(f'started at {time.strftime("%X")}')

# 使用gather可将多个协程传入

await asyncio.gather(

do_after('hello', 1),

do_after('python', 2),

)

print(f'finished at {time.strftime("%X")}')

asyncio.run(gather())

# 运行结果

# started at 23:47:50

# hello

# python

# finished at 23:47:52

两个任务消耗的时间为其中消耗时间最长的任务。

0x03 引用

python until怎么用不了_为何你还不懂得如何使用Python协程相关推荐

  1. python壁纸超清全面屏_你见过的最全面的python重点

    首先和大家说个对不起,由于总结了太多的东西,所以篇幅有点长,这也是我"缝缝补补"总结了好久的东西,对于Nginx的东西我没总结在这里,大家可以Python聚焦看,点击直达专栏哦. ...

  2. python 多线程和协程结合_一文讲透 “进程、线程、协程”

    本文从操作系统原理出发结合代码实践讲解了以下内容: 什么是进程,线程和协程? 它们之间的关系是什么? 为什么说Python中的多线程是伪多线程? 不同的应用场景该如何选择技术方案? ... 什么是进程 ...

  3. python中常用的序列化模块_第六章 常用模块(5):python常用模块(序列化模块:pickle,json,shelve,xml)...

    6.3.7 序列化模块 (pickle,json,shelve,xml) 文件写入,数据传输时,我们都是以字符串形式写入的(因为字符串可以encode成bytes). 那其他类型(比如字典,列表等)想 ...

  4. python语言的实验心得体会_国产编程语言木兰被质疑换皮Python:当事人承认夸大、道歉...

    近日有报道称,中科院计算所的计算机体系结构国家重点实验室编译组成功研发了国产编程语言"木兰"(Mulan),主要面向物联网应用,可以成倍地提高应用执行效率,并且全面支持国产龙芯处理 ...

  5. bit是python最快的bitcoin库_新的Bitcoinpython节点比以前的Python库快100倍

    3月27日,Bitcoin Cash(BCH)粉丝认识了用Python编程语言编写的新BCH完整节点. 该项目名为Bitcoinpython,是一个现代化的BCH库,其创建者声称它是速度最快的Pyth ...

  6. python如何赚外快 淘宝_业余时间怎么赚外快?用Python赚钱的5个方法!

    Python作为一门编程语言,一门技术,就一定能够为我们所用,至少赚个外快是绝对没有问题的. 渠道一:淘宝搜python程序 可以到淘宝上搜,Python程序,到相应的店里找客服,就说你想做程序开发, ...

  7. golang 数组 最后一个_面试必问:Golang高阶Golang协程实现原理

    1 01 引言 实现并发编程有进程,线程,IO多路复用的方式.(并发和并行我们这里不区分,如果CPU是多核的,可能在多个核同时进行,我们叫并行,如果是单核,需要排队切换,我们叫并发). 1.1 进程和 ...

  8. python class 是否存在某个变量_一文抵十课,考验你的Python变量是否理解透彻了

    变量是编程的基础概念,Python 的变量也看似很简单,但是如果理解不当,生搬硬套,可能会遇到一些麻烦. 下面用 10 个代码示例展示 Python 的 变量 本质. 快来看看你对 Python 的变 ...

  9. python 两个数据框合并计算_一文掌握Excel、SQL、Python【数据合并】大法!

    文章最后有录制的讲解视频,如果看文章不舒服的话,可以直接滑到文末看视频,希望你能喜欢~ 在工作中,会经常遇到将多张表合并为一张表的需求:在面试的时候,有时也会被面试官问到左连接.右连接.内连接的区别. ...

最新文章

  1. php 对接 asp,PHP模拟asp中response类实现方法
  2. Java入门算法(树篇)
  3. java创建临时文件_用Java创建一个临时文件
  4. mysql数据库密码为空_注意MySQL 数据库用户root密码为空_MySQL
  5. linux中对shell变量内容的修改和替换
  6. 蓝桥杯 ADV-221 算法提高 7-1用宏求球的体积
  7. 单元刚度矩阵与刚度方程
  8. 如何成为一名优秀的技术工程师?
  9. Tourist Behaviour Themes and Conceptual Schemes--Chapter 1 Studying Tourist Behaviour
  10. 查看计算机真实用户名(username)
  11. 彼得林奇:全球理财师中的NO.1
  12. 【Linux 性能优化】利用perf和CPU使用率定位异常函数
  13. 【随笔1】石榴红,依旧。
  14. 大数据与人工智能人脸识别
  15. 喜剧院线电影《大夫我没病》在京开机
  16. 统一身份认证(SSO/AD域/LDAP)
  17. ARM Cortex M3M4处理器复位流程
  18. 手机访问电脑文件ftp服务器,怎么用手机直接访问电脑上的视频文件图片等?
  19. Python的IDEL增加清屏功能
  20. Android图片素描效果

热门文章

  1. 关于python导入模块和package的一些深度思考
  2. 不要纠结于过去发生的事
  3. Docker配置远程连接端口
  4. python编程问题--第四次
  5. Pytorch两种模型保存方式
  6. LeetCode简单题之爬楼梯
  7. AI芯片体系结构目标图形处理
  8. 【CV】Anaconda 安装教程|CSDN创作打卡
  9. 【hexo】搭建个人bolg日志 day1
  10. Python 实现九九乘法表