Python сопрограмм дает неизвестный None по урожайности - PullRequest
1 голос
/ 19 марта 2020

Я пытаюсь построить конвейер некоторых математических операций на основе ввода пользователя и пытаюсь распечатать результат накопления для этих операций. Например, вводом будет список операций, затем число цифр c входов и затем чисел, подобных этому:

[square, accumulate]
3
1
2
3

Это должно возвращать что-то вроде:

1
5
14

Сначала он печатает 1 как 1 * 1, затем добавляет 1 к результату 2 * 2, дающему 5, затем добавляет его к 3 * 3, дающему 14. Но с моим подходом что-то не так, ввод второго числа всегда поворачивается в значение None, и я не знаю почему. Я застрял, получив:

1
None
10

Есть идеи? Вот мой код:

import math
import os
import random
import re
import sys

def printer():
    while True:
        x = yield
        print(x)

def co_generator(n):
    for _ in range(n):
        x = int(input())
        yield x

def get_root():
    while True:
        number = (yield)
        yield math.floor(math.sqrt(number))

def get_square():
    number = 0
    while True:
        number = (yield)
        yield number**2

def accumulator():
    acum = 0
    while True:
        acum += (yield)
        yield acum

def operations_pipeline(numbers, operations, print_acum):
    for num in numbers:
        for i, w in enumerate(operations):
            num = w.send(num)
        print_acum.send(num)
    for operation in operations:
        operation.close()
    print_acum.close()

if __name__ == '__main__':
    order = input().strip()
    n = int(input())

    numbers = co_generator(n)

    print_acum = printer()
    next(print_acum)

    root = get_root()
    next(root)

    accumulate = accumulator()
    next(accumulate)

    square = get_square()
    next(square)

    operations_pipeline(numbers, eval(order), print_acum)

Ответы [ 2 ]

0 голосов
/ 19 марта 2020

Вы пишете свой код, как будто (yield) получает значение, а yield whatever отправляет его. Это не так, как это работает. Все yield s отправляют значение и получают его.

Когда генератор выполняет yield, он делает это в два этапа. Во-первых, аргумент yield становится возвращаемым значением текущего вызова next или send, и генератор приостанавливает выполнение. Если аргумента нет, используется None. Вот откуда приходят None s

Секунда, когда выполняется другой next или send, генератор не работает, и аргумент send (или None, если next был used) становится значением выражения yield.

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

def get_square():
    number = 0
    while True:
        number = yield number**2

В качестве альтернативы, если вы хотите использовать отдельные yield s для отправки и получения значений на конце генератора, тогда вам нужно использовать отдельные send (или next) вызовы получать и отправлять значения на другом конце и игнорировать None s. Например,

w.send(num)
num = next(w)

вместо num = w.send(num), поэтому operations_pipeline будет выглядеть как

def operations_pipeline(numbers, operations, print_acum):
    for num in numbers:
        for w in operations:
            w.send(num)
            num = next(w)
        print_acum.send(num)
    for operation in operations:
        operation.close()
    print_acum.close()
0 голосов
/ 19 марта 2020

Я полагаю, вам нужно добавить вызов к next в вашем operations_pipeline коде здесь:

def operations_pipeline(numbers, operations, print_acum):
    for num in numbers:
        for i, w in enumerate(operations):
            num = w.send(num)
            next(w) # <<<--- this

Без этого я не верю, что когда-либо возвращался в get_square второй время, предполагая, что ваш первый ввод был [square, accumulate].

...