Python中协程实现学习笔记

最近在看协程这个话题,完全处于好奇,简单了解了下Python中的表示形式,这里仅仅是对Python中的协程很浅很浅的了解。

一、Python中的协程背景

0、Python3.4之前官方不支持协程(第三方的库支持gevent、Tornado)。

1、3.4版本内置了异步I/O库asyncio, 通过装饰器@asyncio.coroutine和yield from表达式实现协程。

2、3.5版本开始引入async/await语法,用来简化协程的使用并且便于理解,实际是3.4版本实现协程的语法糖。

3、3.7版本增加了asyncio.run()方法,更方便的实现协程。

二、Python3.5+协程原理

0、async语法及协程函数
使用 async def 语法定义的函数总是为协程函数,即使它们不包含 await 或 async 关键字。

1、await语法
挂起协程的执行以等待一个可等待对象(awaitable), 比如协程、任务、Future。

2、asyncio.create_task()函数用了并发执行多个协程任务。

三、Python3.7+协程应用

关于Python不同版本协程的实现,参考[掘金:理解Python的协程(Coroutine)]。这里主要整理3.7+中的应用。

以下示例来自Python官网, 协程与任务

0、执行一个协程序

import asyncio

async def main():
    print('hello')
    await asyncio.sleep(1)
    print('python')

asyncio.run(main())

以上代码保存文件test1.py, 并执行, 先打印’hello’, 隔1s打印’python’

python3 test1.py

hello
python

1、等待一个协程

import asyncio
import time

async def say_after(delay, what):
    await asyncio.sleep(delay)
    print(what)

async def main():
    print(f"started at {time.strftime('%X')}")

    await say_after(1, 'hello')
    await say_after(2, 'python')

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

asyncio.run(main())

以上代码保存文件test2.py, 并执行, 等待1s打印’hello’, 隔2s打印’python’, 开始和结束之间用了3s

python3 test2.py

started at 21:53:47
hello
world
finished at 21:53:50

2、多协程并发执行

import asyncio
import time

async def say_after(delay, what):
    await asyncio.sleep(delay)
    print(what)

async def main():
    task1 = asyncio.create_task(
        say_after(1, 'hello'))

    task2 = asyncio.create_task(
        say_after(2, 'python'))

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

    # Wait until both tasks are completed (should take
    # around 2 seconds.)
    await task1
    await task2

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

asyncio.run(main())

以上代码保存文件test3.py, 并执行, 等待1s打印’hello’, 隔1s打印’python’, 开始和结束之间用了2s

python3 test3.py

started at 21:58:56
hello
python
finished at 21:58:58

四、问题

0、在运行代码test1.py的时候,提示AttributeError: partially initialized module ‘asyncio’ has no attribute ‘run’ (most likely due to a circular import)。
原因:文件名一开始命名为asyncio.py, 所以出现了循环import。

类似的问题,如果用低于3.7版本来执行,也是提示,参考stackoverflow:Python3.6 AttributeError: module ‘asyncio’ has no attribute ‘run’

五、总结

0、Python中内置了协程的功能,实现多协程很方便,比PHP简洁太多,不过没有Lua中协程易于理解。

1、Python不同版本差异真的很大。

2、Python的交互式运行环境可以写多行代码,PHP只能写一行。

python3

Python 3.8.3 (default, May 17 2020, 17:00:38)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-28)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import asyncio
>>> async def main():
...     print('hello')
...     print('python')
...
>>> asyncio.run(main())
hello
python

PHP回车换行就相当于执行了

php -a
Interactive shell

php > class Solution() {}
PHP Parse error:  syntax error, unexpected '(', expecting '{' in php shell code on line 1
php >

参考:
掘金:
GeeksforGeeks:Coroutine in Python
理解 Python asyncio

发表评论

电子邮件地址不会被公开。 必填项已用*标注