如何使函数异步运行?

时间:2021-02-21 07:05:26

标签: python asynchronous async-await python-asyncio

我最近一直在尝试使用 Python 中的异步函数,我想知道如何将同步函数变成异步函数。

例如,有通过 google api pygoogletranslation 进行翻译的库。人们很可能想知道,如何异步翻译许多不同的单词。当然,你可以把它放在一个请求中,但是google api会认为它是一个文本并进行相应的处理,这会导致错误的结果。

怎么能转这个代码:

from pygoogletranslation import Translator
translator = Translator()
translations = []
words = ['partying', 'sightseeing', 'sleeping', 'catering']
for word in words:
    translations.append(translator.translate(word, src='en', dest='es'))
print(translations)

进入这个:

from pygoogletranslation import Translator
import asyncio
translator = Translator()
translation_tasks = []
words = ['partying', 'sightseeing', 'sleeping', 'catering']
for word in words:
    asyncio.create_task(translator.translate(word, src='en', dest='es'))
translations = asyncio.run(
    asyncio.gather(translation_tasks, return_exceptions=True) 
)
print(translations)

考虑到函数 translate 没有内置的 async 实现?

3 个答案:

答案 0 :(得分:1)

您必须创建一个 async 函数,然后运行它。尽管如果 translate 没有内置 async 支持或被阻塞,使用 async 不会使其更快。最好按照评论中的建议使用多线程/多处理。

async def main():
    async def one_iteration(word):
        output.append(translator.translate(word, src='en', dest='es'))
    coros = [one_iteration(word) for word in words]
    await asyncio.gather(*coros)
asyncio.run(main())

答案 1 :(得分:1)

正如其他答案中提到的,调用阻塞函数对 ayncio 没有用。在这种特殊情况下,我建议您使用 google-cloud-translate,它是 Google 的官方翻译库。

你本可以在你当前的图书馆做这样的事情:

async def do_task(word):
    return translator.translate(word, ...)

def main():
    # Create translator
    ...
    asyncio.gather(do_task(word) for word in [])

但这只会在没有 asyncio 的情况下以相同的方式运行任务。 asyncio 的真正好处在于,当有待处理或等待的事情时,它可以做其他事情。例如,在等待服务器响应时,它可以发送另一个请求。

Python 如何知道某些工作正在等待处理?仅当函数(此处为协程)通过 await 关键字通知事件循环时。所以你肯定需要使用一个原生支持异步操作的库。上面提到的google-cloud-translate就是这样一个库。你可以这样做:

from google.cloud import translate


async def main():
    # Async-supported google translator client
    client = translate.TranslationServiceAsyncClient()
    words = ['partying', 'sightseeing', 'sleeping', 'catering']
    results = await asyncio.gather(*[client.translate_text(parent=f"projects/{project_name}", contents=[word], source_language_code="en", target_language_code="es") for word in words])
    print(results)

asyncio.run(main())

你可以看到这个客户端实际上是将字符串列表作为输入,所以你可以在这里直接传递字符串列表。根据 docs,这个限制是 1024。所以如果你的列表更大,你必须使用这个 for 循环。

不过,您可能需要为此客户端设置凭据等,这超出了本问题的范围。

答案 2 :(得分:0)

要使函数异步,您需要使用 async def 定义它并将其更改为使用其他异步函数来处理可能阻塞的任何事情 - 例如,您将使用 {{} 代替 requests 1}} 等。努力的重点是该函数可以与其他此类函数一起由事件循环执行。每当异步函数需要等待某事时,如 aiohttp 关键字所指示的那样,它会暂停到事件循环并给其他人一个执行的机会。事件循环将无缝协调可能大量此类异步函数的并发执行。见例如this answer 了解更多详情。

如果您依赖的关键阻塞函数没有异步实现,您可以使用 run_in_executor(或者,从 Python 3.9 开始,asyncio.to_thread)使其异步。但是请注意,此类解决方案是“欺骗”,因为它们在幕后使用线程,因此它们不会提供通常与 asyncio 相关的好处,例如扩展线程池中线程数之外的能力,或取消执行协程。

相关问题