Как я могу выдавать уведомления и возвращать результат из функции? (Python) - PullRequest
0 голосов
/ 06 сентября 2018

Мне нужно создать функцию, которая выполняет тяжелую работу во внутренних вызовах. Эта функция должна быть генератором, потому что я использую Server-Sent Events. Итак, я хочу, чтобы эта функция уведомляла о ходе вычислений, используя «yield». После этого эта функция должна передать результат родительской функции, чтобы продолжить другие вычисления.

Я бы хотел что-то вроде этого:

def hardWork():
    for i in range(N):
        # hard work
        yield 'Work done: ' + str(i)

    # Here is the problem: I can't return a result if I use a yield
    return result             

def generator():
    # do some calculations
    result = hardWork()
    # do other calculations with this result
    yield finalResult

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

Есть ли другое решение?

Спасибо!

EDIT

Я думал что-то вроде:

def innerFunction(gen):
    calc = 1

    for iteration in range(10):
        for i in range(50000):
            calc *= random.randint(0, 10)
        gen.send(iteration)

    yield calc


def calcFunction(gen):
    gen2 = innerFunction(gen)
    r = next(gen2)

    gen.send("END: " + str(r + 1))
    gen.send(None)


def notifier():
    while True:
        x = yield
        if x is None:
            return
        yield "Iteration " + x


def generator():
    noti = notifier()
    calcFunction(noti)
    yield from noti


for g in generator():
    print(g)

Но я получаю эту ошибку:

TypeError: can't send non-None value to a just-started generator

Ответы [ 2 ]

0 голосов
/ 06 сентября 2018

Мое предложение было бы включить вашу функциональность в класс.

def Worker:

    def hardWork(self):
        self.Finished = False
        for i in range(10):
            yield 'Work done: ' + str(i)
        self.Finished = True
        self.Result = 'result'

    def generator(self):
        while (not self.Finished):
            print(next(self.hardWork()))
        return self.Result

Это будет иметь ту функциональность, которую вы хотите, не беспокоясь о программировании вашей логики вокруг логики броска-исключения-исключения.

0 голосов
/ 06 сентября 2018

До Python3.5: генераторы

Это решение также работает для более поздних версий Python, хотя async def, новое в Python3.5, кажется более подходящим для вашего варианта использования. См. Следующий раздел .

Значения, полученные генератором, получены путем итерации или с использованием next. Значение, возвращаемое в конце, сохраняется в атрибуте value исключения StopIteration, которое указывает конец генератора. К счастью, это не так сложно восстановить.

def hardWork():
    output = []

    for i in range(10):
        # hard work
        yield 'Doing ' + str(i)
        output.append(i ** 2)

    return output

def generator():
    # do some calculations
    work = hardWork()

    while True:
        try:
            print(next(work))
        except StopIteration as e:
            result = e.value
            break

    yield result

Пример

foo = generator()
print(next(foo))

выход

Doing 0
Doing 1
Doing 2
Doing 3
Doing 4
Doing 5
Doing 6
Doing 7
Doing 8
Doing 9
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Python3.5 +: async def

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

import asyncio

async def hardWork():
    output = []

    for i in range(10):
        # do hard work
        print('Doing ', i)
        output.append(i**2)

        # Break point to allow the event loop to do other stuff on the side
        await asyncio.sleep(0)

    return output

async def main():
    result = await asyncio.wait_for(hardWork(), timeout=None)
    print(result)

loop = asyncio.get_event_loop()

loop.run_until_complete(main())

выход

Doing  0
Doing  1
Doing  2
Doing  3
Doing  4
Doing  5
Doing  6
Doing  7
Doing  8
Doing  9
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
...