用Telegram 机器人库 Telethon的时候发现他家的 API 接口走的都是 async。
这就来学一下 Python 的 async 和 await (官方文档),需要 Python 版本3.7以上。
import asyncio
async def f():
print('start')
await asyncio.sleep(1) # sleep 1s
print('end')
asyncio.run(f())
f()
返回了一个协程对象,asyncio.run
调用会执行这个对象,输出start
和end
之间会等一秒钟。 async
和await
的语法类似js。
import asyncio
async def f():
print('start')
await asyncio.sleep(1) # sleep 1s
print('end')
async def main():
await f()
await f()
asyncio.run(main())
这份代码会调用f()
两次,但并不会并发(看起来也是这样),那怎样让两个f()
同时执行呢?
import asyncio
async def f():
print('start')
await asyncio.sleep(1) # sleep 1s
print('end')
async def main():
await asyncio.gather(f(), f())
asyncio.run(main())
用 asyncio.gather()
可以并发执行一些协程对象。协程函数如果有返回值,也可以通过gather
的返回值拿到。
asyncio.wait_for(f(), timeout=0.5)
可以限制等待的时长,到时间会抛出一个asyncio.TimeoutError
异常。
多线程
如果有阻塞线程的行为的话,也可以用asyncio.to_thread(func)
把在其他线程调用函数func这个事情包装成一个协程对象可等待对象。例如(Python >= 3.9):
import os, asyncio
ip = '101.6.?.%d'
def test(x):
return os.system(f'ping {ip%x} -c 1 -W 1')
async def main():
tasks = [asyncio.to_thread(test, x=i) for i in range(256)]
results = await asyncio.gather(*tasks)
aval_ip = [i for i in range(256) if results[i]==0]
return aval_ip
asyncio.run(main())
os.system
是阻塞的,利用gather
可以并发扫描某个网段下哪些机器回复ping
。
ps. Telethon库提供了同步的接口,通过import sync
启用。