Асинхронное программирование 101: краткая история асинхронного ожидания Python

Python

Эта статья относится к:

выход и выход из

Сначала изучим или рассмотримyieldиyield fromиспользование. Если вы уверены, что завершили свое понимание, вы можете перейти к следующему разделу.

Python 3.3 предлагает новый синтаксис:yield from.

yield from iterator

По сути, это эквивалентно:

for x in iterator:
    yield x

В следующем примере дваyield fromскладываются, и комбинация дает большоеiterable(пример изОфициальный сайт 3.3 релиз):

>>> def g(x):
...     yield from range(x, 0, -1)
...     yield from range(x)
...
>>> list(g(5))
[5, 4, 3, 2, 1, 0, 1, 2, 3, 4]

пониматьyield fromВажно для следующей части. хочу полностью понятьyield from, давайте посмотрим на официальный пример:

def accumulate():
    tally = 0
    while 1:
        next = yield
        if next is None:
            return tally
        tally += next


def gather_tallies(tallies):
    while 1:
        tally = yield from accumulate()
        tallies.append(tally)

tallies = []
acc = gather_tallies(tallies)
next(acc) # Ensure the accumulator is ready to accept values

for i in range(4):
    acc.send(i)
acc.send(None) # Finish the first tally

for i in range(5):
    acc.send(i)
acc.send(None) # Finish the second tally
print(tallies)

Я также специально для этого записал видео, вы можете посмотреть его с текстом, а можете открыть pycharm и любые средства отладки, чтобы отладить самостоятельно. ссылка на видео

Разбираемся вместе:

отacc = gather_tallies(tallies)Эта строка начинается сgather_talliesВ функции есть выход, поэтому она не будетwhile 1Выполняется немедленно (как видно из видео, акк — это тип генератора).

next(acc):

next() перейдет к следующему выходу или сообщит об ошибке StopIteration.

next(acc)Введите тело функции collect_tallies, оно есть в collect_tallies.yield from accumulate(), next(acc) не остановится на этом, а войдет в "подгенератор" аккумулировать, а затемnext = yieldгде, встретилyield, затем приостановите функцию и вернитесь.

for i in range(4):
    acc.send(i)

пониматьacc.send(value)Какая польза:

  • Шаг 1: Вернитесь туда, где вы остановились
  • Шаг 2: Присвойте значение valuexxx = yieldсерединаxxx, в данном примере этоnext.

accumulateЦикл while в функции, судя поnextЗначение None определяет выход из цикла. существуетfor i in range(4)В этом цикле for ни i не равно None, поэтому цикл while не прерывается. Однако, согласно тому, что мы сказали ранее:next() перейдет к следующему месту выхода и остановится, этот цикл делает круг и снова встречаетсяyield, поэтому он приостанавливает функцию и возвращает управление основному потоку.

Для уточнения: для накопления его бесконечный цикл не закончен, и в следующий раз, когда он возобновит свою работу через next(), он все еще будет выполнять свой бесконечный цикл. Для collect_tallies егоyield from accumulate()Это еще не закончено. Для всей программы действительно существует несколько переходов между основным процессом и телом функции накопления.

Следующий взгляд на первыйacc.send(None): В настоящее времяnextЗначение переменной становитсяNone,if next is Noneусловие верно, то возвратtallyк предыдущей функции. (Посчитайте, получается 0 + 1 + 2 + 3 = 6). Это возвращаемое значение присваиваетсяgather_talliesсерединаgally. Здесь следует отметить, что,gather_talliesБесконечный цикл еще не закончился, так что звоните в это времяnext(acc)не будет сообщатьStopIterationОшибка.

for i in range(5):
    acc.send(i)
acc.send(None) # Finish the second tally

Эта часть аналогична предыдущей логике. acc.send(i) войдет первымgather_tallies, затем введитеaccumulate, присвойте значениеnext.acc.send(None)Остановите петлю. Окончательный счет равен 10 (0 + 1 + 2 + 3 + 4).

Окончательный итоговый список таков:[6,10].

Краткая история развития Python async await

Взгляните на определение Coroutine в Википедии:

Coroutines are computer program components that generalize subroutines for non-preemptive multitasking, by allowing execution to be suspended and resumed.

Дело в томby allowing execution to be suspended and resumed.(чтобы выполнение можно было приостановить и возобновить). С точки зрения непрофессионала, это:

сопрограммы — это функции, выполнение которых вы можете приостановить. (отHow the heck does async/await work in Python 3.5?)

Разве это не генератор?

python2.2 - происхождение генератора

Концепция генератора Python возникла в python2.2 (2001 г.), когда он был исключен.pep255,отЯзык программирования иконокВдохновленный.

Преимущество генераторов в том, что они не занимают место впустую, см. следующий пример:

def eager_range(up_to):
    """Create a list of integers, from 0 to up_to, exclusive."""
    sequence = []
    index = 0
    while index < up_to:
        sequence.append(index)
        index += 1
    return sequence

Если вы используете эту функцию для создания списка 10W, вам нужно дождаться завершения цикла while и возврата. тогда этоsequenceСписок займет 10W элементов пространства. Не говоря уже о трудоемкости (с точки зрения времени, когда список последовательностей можно использовать в первый раз), он занимает много места.

С помощью предыдущей частиyield, немного изменено:

def lazy_range(up_to):
    """Generator to return the sequence of integers from 0 to up_to, exclusive."""
    index = 0
    while index < up_to:
        yield index
        index += 1

Таким образом, ему нужно всего лишь занять пространство одного элемента, и диапазон можно использовать сразу, не дожидаясь его полной генерации.

python2.5 : send stuff back

Некоторые предусмотрительные старики думали,Если мы сможем воспользоваться тем фактом, что генераторы могут приостанавливаться, а затем найти способ добавить возможность отправлять данные обратно, разве это не будет соответствовать определению сопрограмм в Википедии?

Так что естьpep342.

Один упоминается в pep342send()метод, который позволяет нам отправить «материал» обратно в генератор и позволить ему работать. Рассмотрим следующий пример:

def jumping_range(up_to):
    """Generator for the sequence of integers from 0 to up_to, exclusive.

    Sending a value into the generator will shift the sequence by that amount.
    """
    index = 0
    while index < up_to:
        jump = yield index
        if jump is None:
            jump = 1
        index += jump


if __name__ == '__main__':
    iterator = jumping_range(5)
    print(next(iterator))  # 0
    print(iterator.send(2))  # 2
    print(next(iterator))  # 3
    print(iterator.send(-1))  # 2
    for x in iterator:
        print(x)  # 3, 4

здесьsendОтправьте «материал» в генератор, назначьте его для перехода, а затем определите, является ли переход «Нет», чтобы выполнить соответствующую логику.

python3.3 yield from

Начиная с Python 2.5, до версии Python 3.3 в генераторы не вносилось никаких существенных улучшений.pep380. Это бодрящее предложение предлагаетyield fromЭто можно понимать как синтаксический сахар, что делает написание генераторов более лаконичным:

def lazy_range(up_to):
    """Generator to return the sequence of integers from 0 to up_to, exclusive."""
    index = 0
    def gratuitous_refactor():
        nonlocal index
        while index < up_to:
            yield index
            index += 1
    yield from gratuitous_refactor()

Мы уже подробно говорили о yield from в первом разделе, поэтому здесь я не буду вдаваться в подробности.

асинхронный модуль python3.4

Межстраничное объявление: цикл событий (цикл событий)

Если у вас есть опыт программирования на js, вы должны кое-что знать о цикле обработки событий.

Чтобы понять концепцию, лучше всего покопаться в википедии:

цикл событий "является программной конструкцией, которая ожидает и отправляет события или сообщения в программе" - отEvent loop - wikipedia

Проще говоря, реализация цикла событийКогда произойдет событие А, выполните действие Б. Возьмем в качестве примера цикл событий JavaScript в браузере: вы нажимаете на что-то (происходит событие), оно запускает определенныйonclickфункция (выполняет операцию B).

В Python asyncio обеспечивает цикл обработки событий (вспомнимПример из предыдущего поста), асинциоглавныйОсновное внимание уделяется области сетевых запросов.«Происходит событие A» здесь в основном заключается в том, что сокет может быть записан и сокет может быть прочитан (черезselectorsмодуль).

К этому времени Python прошелConcurrent programmingФорма обладает силой асинхронного программирования.

Concurrent programmingВыполнять только в одном потоке. В блоге go language есть очень хорошее видео:Concurrency is not parallelism, это стоит посмотреть.

асинхронный код в эту эпоху

Асинхронный код для этого периода выглядит так:

import asyncio

# Borrowed from http://curio.readthedocs.org/en/latest/tutorial.html.
@asyncio.coroutine
def countdown(number, n):
    while n > 0:
        print('T-minus', n, '({})'.format(number))
        yield from asyncio.sleep(1)
        n -= 1

loop = asyncio.get_event_loop()
tasks = [
    asyncio.ensure_future(countdown("A", 2)),
    asyncio.ensure_future(countdown("B", 3))]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()

Результат:

T-minus 2 (A)
T-minus 3 (B)
T-minus 1 (A)
T-minus 2 (B)
T-minus 1 (B)

На этот раз с помощьюasyncio.coroutineДекоратор, помечающий функцию, которую может использовать цикл обработки событий asyncio.

Видетьyield from asyncio.sleep(1)Пока что? Вызывая объект asyncio.Futureyield from, передать будущий объект в цикл событий, когда объект ожидает, что что-то произойдет (в этом примере, ожидая asyncio.sleep(1) через 1 с), приостановить функцию и начать делать другие вещи. Когда произойдет что-то, чего ожидает этот объект будущего, цикл обработки событий заметит это, а затем, вызвавsend()способ возобновить с того места, на котором он остановился.

разбить приведенный выше код:

Цикл событий открывает дваcountdown()Вызов сопрограммы, выполняется до тех пор, покаyield from asyncio.sleep(1), который возвращает будущий объект, затем приостанавливается, и цикл обработки событий продолжает отслеживать оба будущих объекта. Через 1 секунду цикл событий отправит () будущий объект сопрограмме, и сопрограмма продолжит работу, распечатываяT-minus 2 (A)Ждать.

python3.5 async await

питон3.4

@asyncio.coroutine
def py34_coro():
    yield from stuff()

В Python 3.5 это можно выразить более кратким синтаксисом:

async def py35_coro():
    await stuff()

Это изменение не имеет большого значения с точки зрения синтаксиса.Что действительно важно, так это повышенный философский статус сопрограмм в Python.До python3.4 асинхронные функции были скорее обычным маркером (декоратором), после этого сопрограммы стали базовым абстрактным базовым классом:class collections.abc.Coroutine.

How the heck does async/await work in Python 3.5?Также упоминается в статьеasync,awaitРеализация лежащего в основе байт-кода не будет подробно обсуждаться здесь, в конце концов, место ограничено.

Думайте об асинхронности и ожидании как об API, а не о реализации

Разработчик ядра Python (и один из моих любимых говорящих о pycon)David M. Beazleyна PyCon Brasil 2015эта речьупоминается в:мы должны положитьasyncиawaitРассматривайте это как API, а не как реализацию.Это,async,awaitне равноasyncio,asyncioпростоasync,awaitреализация . (конечноasyncioсделал возможным асинхронное программирование в Python 3.4, что подтолкнулоasync,awaitвнешний вид)

Он также открыл исходный код проектаGitHub.com/dab EA в/Промо..., базовый механизм цикла событий иasyncioРазные,asyncioиспользуетfuture object,curioиспользуетtuple. В то же время эти две библиотеки имеют разную направленность,asyncioпредставляет собой набор фреймворков,curioОн относительно более легкий, и сами пользователи должны учитывать больше вещей.

How the heck does async/await work in Python 3.5?Также в этой статье есть простой пример реализации событийного цикла, если интересно, можете посмотреть, а если есть время, то реализовать вместе.

в заключении

  • Сопрограмма имеет только один поток.
  • Операционная система планирует процессы и сопрограммы с помощью функций планирования цикла событий.
  • async и await поднимают философский статус сопрограмм в Python на более высокий уровень.

Самое главное чувство: Ничто не является Волшебством. Теперь вы должны получить общее представление о сопрограммах Python.

Если вы действительно любите компьютерные науки, как и я, любите изучать лежащую в их основе логику, обратите внимание на мой общедоступный номер WeChat: