#python #asynchronous #tornado #synchronous
#python #асинхронный #торнадо #синхронный
Вопрос:
Я пишу библиотеку, которая использует Tornado Web tornado.httpclient.AsyncHTTPClient
для выполнения запросов, что дает моему коду async
интерфейс:
async def my_library_function():
return await ...
Я хочу сделать этот интерфейс необязательно последовательным, если пользователь предоставляет kwarg — что-то вроде: serial=True
. Хотя вы, очевидно, не можете вызвать функцию, определенную с async
помощью ключевого слова, из обычной функции без await
. Это было бы идеально, хотя на данный момент почти наверняка невозможно на языке:
async def here_we_go():
result = await my_library_function()
result = my_library_function(serial=True)
Я не смог найти ничего в Интернете, где кто-то придумал хорошее решение для этого. Я не хочу переопределять в основном один и тот же код без awaits
разбрызгивания по всему.
Это то, что можно решить, или для этого потребуется поддержка со стороны языка?
Решение (хотя вместо этого используйте Jesse’s — объяснено ниже)
Приведенное ниже решение Джесси — это в значительной степени то, с чем я собираюсь пойти. В итоге я получил интерфейс, который изначально хотел, используя декоратор. Что-то вроде этого:
import asyncio
from functools import wraps
def serializable(f):
@wraps(f)
def wrapper(*args, asynchronous=False, **kwargs):
if asynchronous:
return f(*args, **kwargs)
else:
# Get pythons current execution thread and use that
loop = asyncio.get_event_loop()
return loop.run_until_complete(f(*args, **kwargs))
return wrapper
Это дает вам этот интерфейс:
result = await my_library_function(asynchronous=True)
result = my_library_function(asynchronous=False)
Я проверил это в списке асинхронной рассылки python, и мне повезло, что Гвидо ответил, и он вежливо отклонил его по этой причине:
Запах кода — возможность вызывать одну и ту же функцию как асинхронно, так и синхронно вызывает большое удивление. Также это нарушает эмпирическое правило, согласно которому значение аргумента не должно влиять на возвращаемый тип.
Приятно знать, что это возможно, хотя и не считается отличным интерфейсом. Гвидо, по сути, предложил ответ Джесси и представил функцию переноса в качестве вспомогательной утилиты в библиотеке вместо того, чтобы скрывать ее в декораторе.
Комментарии:
1. возможно, написать код как сопрограмму на основе генератора, а затем использовать types.coroutine() для создания асинхронного эквивалента?
Ответ №1:
Если вы хотите вызвать такую функцию синхронно, используйте run_until_complete:
asyncio.get_event_loop().run_until_complete(here_we_go())
Конечно, если вы часто делаете это в своем коде, вам следует придумать сокращение для этого оператора, возможно, просто:
def sync(fn, *args, **kwargs):
return asyncio.get_event_loop().run_until_complete(fn(*args, **kwargs))
Тогда вы могли бы сделать:
result = sync(here_we_go)