【问题标题】:Generator returned from function completes prematurely从函数返回的生成器过早完成
【发布时间】:2019-12-11 20:10:14
【问题描述】:

我有以下代码来复用阻塞生成器:

import datetime
import time
import queue
import threading


def blocking1():
    while True:
        time.sleep(1)
        result = "Block1: {}".format(datetime.datetime.now())
        yield result


def blocking2():
    while True:
        time.sleep(2)
        result = "Block2: {}".format(datetime.datetime.now())
        yield result


def multiplex(generators):
    if len(generators) == 1:
        return generators[0]
    elif len(generators) > 1:
        q = queue.Queue()

        def run_one(src):
            for e in src: q.put(e)

        def run_all():
            threads = []
            for src in generators:
                t = threading.Thread(target=run_one, args=(src,))
                t.start()
                threads.append(t)
            for t in threads: t.join()
            q.put(StopIteration)

        threading.Thread(target=run_all).start()
        while True:
            e = q.get()
            if e is StopIteration:
                return
            yield e
    else:
        return []


if __name__ == "__main__":
    # tasks = [("map1: {}".format(e) for e in blocking1()), ("map2: {}".format(e) for e in blocking2())]
    tasks = [("map1: {}".format(e) for e in blocking1())]
    for e in multiplex(tasks):
        print(e)


我想聪明点,万一只有一个生成器,不要进行任何线程生成。只需返回这个单一的生成器(在所有类型仍然匹配之后)

但它不是那样工作的。
程序立即终止(就像这是一个空生成器)

有趣的是以下工作(显示map1... 输出):

import datetime
import time
import queue
import threading


def blocking1():
    while True:
        time.sleep(1)
        result = "Block1: {}".format(datetime.datetime.now())
        yield result


def blocking2():
    while True:
        time.sleep(2)
        result = "Block2: {}".format(datetime.datetime.now())
        yield result


def multiplex(generators):
    if len(generators) == 1:
        return generators[0]
    else:
        return []


if __name__ == "__main__":
    # tasks = [("map1: {}".format(e) for e in blocking1()), ("map2: {}".format(e) for e in blocking2())]
    tasks = [("map1: {}".format(e) for e in blocking1())]
    for e in multiplex(tasks):
        print(e)

区别仅在于删除elif 部分...

有人可以帮我理解发生了什么吗? 我正在使用 Python 3.5.3

【问题讨论】:

  • 你能在你的函数中打印len(generators)吗?
  • 1,为什么要不一样?

标签: python python-3.x generator


【解决方案1】:

你不能(有用地)从一个函数返回一个值,该函数在其主体的任何地方都执行yield(即使returnyields 出现在单独的代码块中,这些代码块在函数的相同执行)。如果您在函数中的任何位置都有yield,那么您正在创建一个生成器函数,而不是一个普通函数。

如果你只有一个发电机,一个很好的解决办法是yield from你唯一的发电机:

def multiplex(generators):
    if len(generators) == 1:
        yield from generators[0] # because this is a generator function, we need to yield here
    elif len(generators) > 1:
        ... # there's a yield in here causing the whole thing to be a generator function!

【讨论】:

  • 谢谢你,但我有同样的评论,为什么切割 elif 部分也可以工作?
  • 是的! python解析器仅通过解析它来决定一个函数是否是生成器,即使没有到达语句。这个答案解释了原因(并提供了一个修复,就像其他答案一样,但至少这个答案解释了原因:))
  • 如果你剪切 elif 块的主体,你将剪切唯一的 yield 语句,因此该函数被编译为普通函数而不是生成器函数。
【解决方案2】:

问题是您返回一个生成器,而不是对其进行迭代。

替换

return generators[0]

yield from generators[0]

【讨论】:

  • 是的,yield from 有效。但是为什么在没有elif... 部分的情况下return generators[0] 也可以工作?
猜你喜欢
  • 1970-01-01
  • 2015-03-11
  • 1970-01-01
  • 2020-04-23
  • 2011-02-24
  • 2012-05-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多