Автор оригинала: Doug Hellmann.
Сопрограммы – это языковые конструкции, предназначенные для параллельной работы. Функция сопрограммы при вызове создает объект сопрограммы, и вызывающий может затем запустить код функции, используя метод send () сопрограммы. Сопрограмма может приостановить выполнение с помощью ключевого слова await с другой сопрограммой. Пока он приостановлен, состояние сопрограммы сохраняется, позволяя ей возобновить работу с того места, где она была остановлена, в следующий раз, когда она будет пробуждена.
Запуск сопрограммы
Есть несколько разных способов запустить сопрограмму из цикла обработки событий asyncio . Самый простой – использовать run_until_complete () , передавая ему сопрограмму напрямую.
asyncio_coroutine.py
import asyncio
async def coroutine():
print('in coroutine')
event_loop asyncio.get_event_loop()
try:
print('starting coroutine')
coro coroutine()
print('entering event loop')
event_loop.run_until_complete(coro)
finally:
print('closing event loop')
event_loop.close()Первый шаг – получить ссылку на цикл событий. Можно использовать тип цикла по умолчанию или создать конкретный класс цикла. В этом примере используется цикл по умолчанию. Метод run_until_complete () запускает цикл с объектом сопрограммы и останавливает цикл, когда сопрограмма завершается возвратом.
$ python3 asyncio_coroutine.py starting coroutine entering event loop in coroutine closing event loop
Возвращение значений из сопрограмм
Возвращаемое значение сопрограммы передается обратно в код, который запускается и ожидает его.
asyncio_coroutine_return.py
import asyncio
async def coroutine():
print('in coroutine')
return 'result'
event_loop asyncio.get_event_loop()
try:
return_value event_loop.run_until_complete(
coroutine()
)
print('it returned: {!r}'.format(return_value))
finally:
event_loop.close()В этом случае run_until_complete () также возвращает результат сопрограммы, которую она ожидает.
$ python3 asyncio_coroutine_return.py in coroutine it returned: 'result'
Цепочка сопрограмм
Одна сопрограмма может запускать другую сопрограмму и ждать результатов. Это упрощает разложение задачи на повторно используемые части. В следующем примере есть две фазы, которые должны выполняться по порядку, но могут выполняться одновременно с другими операциями.
asyncio_coroutine_chain.py
import asyncio
async def outer():
print('in outer')
print('waiting for result1')
result1 await phase1()
print('waiting for result2')
result2 await phase2(result1)
return (result1, result2)
async def phase1():
print('in phase1')
return 'result1'
async def phase2(arg):
print('in phase2')
return 'result2 derived from {}'.format(arg)
event_loop asyncio.get_event_loop()
try:
return_value event_loop.run_until_complete(outer())
print('return value: {!r}'.format(return_value))
finally:
event_loop.close()Ключевое слово await используется вместо добавления новых сопрограмм в цикл, поскольку поток управления уже находится внутри сопрограммы, управляемой циклом, поэтому нет необходимости указывать циклу управлять новым сопрограммы.
$ python3 asyncio_coroutine_chain.py
in outer
waiting for result1
in phase1
waiting for result2
in phase2
return value: ('result1', 'result2 derived from result1')Генераторы вместо сопрограмм
Функции сопрограмм являются ключевым компонентом дизайна asyncio . Они предоставляют языковую конструкцию для остановки выполнения части программы, сохранения состояния этого вызова и повторного входа в состояние в более позднее время, которые являются важными возможностями для инфраструктуры параллелизма.
Python 3.5 представил новые языковые функции для определения таких сопрограмм изначально с использованием async def и для передачи управления с помощью await , а примеры для asyncio используют преимущества новая функция. Более ранние версии Python 3 могут использовать функции генератора, заключенные в декоратор asyncio.coroutine () и yield from для достижения того же эффекта.
asyncio_generator.py
import asyncio
@asyncio.coroutine
def outer():
print('in outer')
print('waiting for result1')
result1 yield from phase1()
print('waiting for result2')
result2 yield from phase2(result1)
return (result1, result2)
@asyncio.coroutine
def phase1():
print('in phase1')
return 'result1'
@asyncio.coroutine
def phase2(arg):
print('in phase2')
return 'result2 derived from {}'.format(arg)
event_loop asyncio.get_event_loop()
try:
return_value event_loop.run_until_complete(outer())
print('return value: {!r}'.format(return_value))
finally:
event_loop.close()В предыдущем примере воспроизводится asyncio_coroutine_chain.py с использованием функций генератора вместо собственных сопрограмм.
$ python3 asyncio_generator.py
in outer
waiting for result1
in phase1
waiting for result2
in phase2
return value: ('result1', 'result2 derived from result1')