Python 異步調用命令行工具

相關閱讀:Python 異步網絡爬蟲 I | Python 異步網絡爬蟲 IIhtml

當你在本身的 Python 程序中採用了基於事件循環的異步編程方法以後,你就會發現本身不自覺地被其緊緊吸引住,並非說這一方法多麼棒,而是由於你不得不想辦法保證程序中的任意環節都不能是阻塞的!python

例如當前的場景是但願從 MongoDB 中讀取每一條未處理過的數據,下載並保存其中的圖片信息,而後更新數據庫的內容。Python 經常使用的 MongoDB 異步驅動是 Motor:shell

結合 asyncio 使用方法以下:數據庫

import motor.motor_asyncio
import asyncio

client = motor.motor_asyncio.AsyncIOMotorClient()
db = client.test_database

async def run():
    async for mm in db.test_database.find({"status": 0}):
        print(mm['img_src'])
        # Download Image Here
        # dl_img(mm['img_src'])

        await db.test_database.update({"_id": mm['_id']}, {"$set": {"status":1}})

loop = asyncio.get_event_loop()
loop.run_until_complete(run())複製代碼

此時若是 dl_img() 處的操做是阻塞的,那麼異步處理就沒有意義了。固然這裏依然能夠藉助異步網絡請求庫 aiohttp 來實現圖片下載:編程

async with session.get(img) as resp:
    with open(img.split("/")[-1], 'wb') as fd:
        while True:
            chunk = await resp.content.read(1024)
            if not chunk:
                break
            fd.write(chunk)複製代碼

固然也能夠不須要本身動手下載,直接調用系統命令行工具(例如 wget)來完成下載任務。Python 經過 subprocess 標準庫實現系統命令調用(取代舊的os.system(cmd)),執行下載任務只須要:網絡

import subprocess as sb

sb.run(['wget', img], shell=True)複製代碼

可是這種調用方式是沒法直接在asyncio的事件循環中使用的,可是asyncio提供了對應的 subprocess接口session

asyncio.create_subprocess_exec(*args, ...)
asyncio.create_subprocess_shell(cmd, ...)複製代碼

這兩個方法均返回一個 asyncio.subprocess.Process 實例,而它的接口設計徹底模仿了 subprocess.Popen(上面提到 subprocess.run()的底層實現),所以很容易將其用法移植到事件循環中:異步

async def dl_img(src):
    dl = await asyncio.create_subprocess_shell('wget {} -O {}'.format(src, src.split("/")[-1])
    await dl.wait()複製代碼

除了上面場景中的用法,也能夠直接將命令行的執行做爲任務放入事件循環:async

loop = asyncio.get_event_loop()
sb = asyncio.create_subprocess_shell('exit 7', loop=loop)
proc = loop.run_until_complete(sb)
exitcode = loop.run_until_complete(proc.wait())複製代碼

小結

在 Python 異步編程的意義就在於不要讓 CPU 堵在 IO 上,所以須要在每一處涉及到阻塞的操做都須要注意使用正確的異步方法,而一旦這些操做被封裝成異步的 Task 以後,其後續的調度執行就無需再顧慮了。異步編程


福利時間到,沒時間解釋了,趕忙上車!

相關文章
相關標籤/搜索