Оба варианта полностью верны, но если есть сомнения, переходите ко второму. Это сделает кристально ясным для вызывающего абонента, что он имеет дело с сопрограммой, и только сопрограммой.
Существует техническая разница между ними, но она становится очевидной только в том случае, если вызывающая сторона не ожидает ее сразу. Рассмотрим следующий код:
aw = my_async_function()
# ... do something here ...
result = await aw
В вашем первом примере «некоторый код синхронизации» будет запущен, как только вы вызовете функцию, поэтому, если у нее есть побочные эффекты, они будут видны в «сделать что-то здесь». Во втором примере, просто вызывая my_async_function()
, совершенно не имеет побочных эффектов, он только создает объект сопрограммы. В этом варианте «некоторый код синхронизации» выполняется только после await aw
, т.е. после того, как «сделать что-то здесь» уже выполнено.
Определение сопрограммы как async def
гарантирует вызывающей стороне, что код синхронизации не будет запущен до фактического ожидания результата сопрограммы, что может быть важной гарантией. Даже если функция не является async
и просто возвращает ожидаемое, было бы разумно вести себя , как если бы поддерживал эту гарантию, по крайней мере, насколько вызывающая сторона может ее наблюдать.
Хорошо известным примером, который не придерживается этого принципа, является run_in_executor
, который немедленно передает вызываемое в пул потоков. По этой причине run_in_executor
никогда нельзя сделать действительной сопрограммой, потому что это нарушит код, вызывающий его, не беспокоясь на await
(или код, ожидающий только позже, по любой причине).