В своем исследовании я вижу общее мнение, что правильный способ ввода асинхронной функции — это Callable[..., Awaitable[Any]].
В Pycharm я пробую это и получаю эту проблему при переходе на asyncio.create_task
import asyncio
from typing import Callable, Awaitable, Any
def fff(ccc: Callable[..., Awaitable[Any]]):
return asyncio.create_task(ccc())
Это проблема с Pycharm, или я должен по-другому печатать свои асинхронные функции?
Ффф? Я просто использую это, чтобы обернуть код, чтобы продемонстрировать подсказку типа. Меня это не волнует - это ССС
Не могли бы вы показать свою функцию ccc? или хотя бы подпись его?
Разобьём всё на части:
async def ccc():
return "Hello World"
type(ccc) # <--- function
async def ccc():
return "Hello World"
type(ccc()) # <--- coroutine, not str!
Это потому, что эту асинхронную функцию нужно ожидать, чтобы действительно вернуть то, что мы изначально хотели, чтобы она вернула — строку.
По сути, вызов сопрограммы внутри асинхронной функции ничего не даст, как указано в документах:
Мы говорим, что объект является ожидаемым, если его можно использовать в выражении ожидания. Многие асинхронные API предназначены для приема ожидаемых значений.
Это означает, что ваша функция ccc действительно вызывается, которая получает некоторые параметры «%d» (отсюда многоточие), которая возвращает сопрограмму.
Мягко говоря, подсказка типа выглядит примерно так:
Callable[..., Coroutine]
И вы можете быть более явным с Coroutine, поскольку я не знаю, что возвращает ccc.
Более того, я не уверен, как вы хотите вызывать fff, поскольку он не асинхронный, вам нужно будет сделать его таким и дождаться asyncio.create_task как такового:
import asyncio
from typing import Callable, Coroutine
async def ccc():
print("Hello")
await asyncio.sleep(5)
print("World")
return 12345
async def fff(random_callable: Callable[..., Coroutine]):
result = await asyncio.create_task(random_callable())
print(f"{result=}")
if __name__ == '__main__':
el = asyncio.get_event_loop()
el.run_until_complete(fff(ccc))
el.close()
Примечание. Я создал ccc случайным образом для собственного объяснения, так как вы не указали его в самом вопросе :-)
Правильно понял, спасибо за разъяснение. ccc должен был быть общей функцией async def (как в вашем примере). asyncio.create_task не ожидается и fff не существует async, потому что я создавал минимальный пример фоновой задачи (на самом деле ccc задача будет удалена сборщиком мусора, если я ничего с ней не сделаю). Отредактировал OP, чтобы fff вернул его
Но я также хотел знать, почему ответы (и комментарии), подобные этому, говорят, что вы «должны» использовать Awaitable вместо Coroutine stackoverflow.com/a/59177557/5805389
В моем примере random_callable действительно является вызываемым, и он возвращает сопрограмму, которая сама является ожидаемой в том смысле, что «мы можем ее дождаться», но на самом деле ее питонический тип — Coroutine. Я бы посоветовал прочитать это: stackoverflow.com/questions/36342899/… ветка, поскольку она хорошо объясняет различия между сопрограммами, задачами, sure_future, create_tasks и т. д. (мы могли бы изменить его на random_callable: Callable[..., Awaitable] и просто ждать его напрямую: result = await random_callable()
Согласно документам, подсказка для Coroutine должна иметь такую форму Coroutine[YieldType, SendType, ReturnType], как Генератор. Вы знаете, почему это так? Если вы не определяете асинхронный генератор, я этого не понимаю. Может быть, я открою новый вопрос, если это не простой однострочный ответ
Я думаю, что это должна быть другая тема :-)
@Shuri2060 Вот комментарий GVR к Awaitable vs Coroutine . Функции с async def-ed имеют методы send, throw и close в дополнение к __await__, и они требуются для asyncio.create_task. Проверьте хорошее объяснение здесь , прочитайте PEP . Его взаимодействие с генераторами может быть еще более забавным. И играть в REPL, конечно же: async def foo(): {yield|return} None; dir(foo())
Ваша функция ничего не возвращает