我最近一直在尝试使用 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
实现?
答案 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 相关的好处,例如扩展线程池中线程数之外的能力,或取消执行协程。