2015-12-05 2 views
3

Мне интересно, что лучше всего подходит для написания сопрограмм в Python 3. Я разрабатываю базовые методы, которые должны принимать некоторые данные (используя метод .send()), выполнять вычисления на этом входе, и затем выход выход.Coroutines in Python: Best Practices

Первый подход, который я нашел, по существу сделать следующее:

def coroutine(func): 
    data = yield 
    while 1: 
    data = yield func(data) 

Это, кажется, работает, но строка в цикле прогибается мой ум. Кажется, что сначала дается функция, а затем принимает ввод и выполняет назначение после возобновление. Это совершенно неинтуитивно для меня.

Другой подход, который я смотрю на это:

def coroutine(): 
    while 1: 
    data = yield 
    [ do stuff with data here ... ] 
    yield result 

Этот код намного проще для меня, чтобы понять, и это также позволяет мне поставить код прямо в генератор вместо прохождения в функции. Но это раздражает. Каждый фактический вызов генератору (например, «gen.send (2)») должен сопровождаться «gen.send (None)», чтобы довести генератор до следующего выхода.

Мне кажется, что проблема здесь связана с ключевым словом «yield», используемым для двух разных вещей: оператора return и оператора ввода.

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


Обратите внимание: на самом деле, я отправлю несколько значений. Поэтому проблемы с посторонними «g.send (None)» утверждениями ухудшаются.

+1

Мне кажется, что * оба * ваших примеров связаны с передачей функции. Кроме того, вы можете больше объяснить, почему вы хотите использовать генераторы для этого вообще. Ваши примеры настолько просты, что они не иллюстрируют каких-либо причин использования генераторов вместо того, чтобы просто вызвать вызывающий вызов 'func' сам в цикле. Кроме того, если первый пример уже работает на вас, каков ваш фактический вопрос? – BrenBarn

+0

@BrenBarn Я отредактировал вопрос, чтобы подчеркнуть, что второму подходу не нужна функция, переданная, и это то, что я хочу. Мой вопрос: как использовать функции генерации в качестве второго подхода без необходимости постоянно отправлять объекты «Нет», чтобы перейти к правильной инструкции yield. – NotNotLogical

ответ

7

Вы можете сделать это, как в первом примере. Вам просто нужно «делать вещи с данными» внутри цикла. Вот пример:

def coroutine(): 
    data = yield 
    while True: 
    print("I am doing stuff with data now") 
    data = data * 2 
    data = yield data 

Вы можете использовать его как это:

>>> co = coroutine() 
>>> next(co) 
>>> co.send(1) 
I am doing stuff with data now 
2 
>>> co.send(88) 
I am doing stuff with data now 
176 

Вы правы, что yield играет двойную роль, и приносит результат, и принимая значение впоследствии передается в через send , (Аналогично, send играет двойную и взаимодополняющую роль, поскольку каждый вызов send возвращает значение, которое дает генератор.) Обратите внимание на то, что при наличии выражения yield оно сначала дает значение вне, а затем значение из выражения yield становится тем, что sent в впоследствии.

Это может показаться «обратным», но вы можете заставить его быть «форвардами», выполняя его в цикле, как вы уже делали. Идея состоит в том, что вы сначала получаете некоторую начальную ценность (возможно, бессмысленную). Это необходимо, потому что вы не можете использовать send до того, как значение будет получено (поскольку для оценки на отправленное значение не будет yield). Затем каждый раз, когда вы используете yield, вы выдаете «текущее» значение, одновременно принимая ввод, который будет использоваться при вычислении «следующего» значения.

Как я уже упоминал в комментарии, из вашего примера не ясно, почему вы используете генераторы вообще. Во многих случаях вы можете добиться аналогичного эффекта, просто написав класс, который имеет свои собственные методы для передачи вещей и получения информации, а если вы напишете класс, вы можете сделать API всем, что захотите. Если вы решите использовать генераторы, вы должны принять две роли ввода/вывода от send и yield. Если вам это не нравится, не используйте генераторы (или, если вам нужно приостановленное состояние функции, которое они предоставляют, вы можете использовать их, но обернуть их классом, который отделяет отправку от уступки).