我写了这段简单的代码:

def mymap(func, *seq):
  return (func(*args) for args in zip(*seq))

我应该使用上述“return”语句返回生成器,还是使用“yield from”指令,如下所示:
def mymap(func, *seq):
  yield from (func(*args) for args in zip(*seq))

除了“ yield ”与“ yield ”之间的技术差异之外,通常情况下哪种方法更好?

最佳答案

不同之处在于您的第一个mymap只是一个常用函数,
在这种情况下,工厂将退还发电机。一切
调用该函数后,体内的对象将立即执行。

def gen_factory(func, seq):
    """Generator factory returning a generator."""
    # do stuff ... immediately when factory gets called
    print("build generator & return")
    return (func(*args) for args in seq)

第二个mymap也是一个工厂,但是它也是一个生成器
本身,来自内部的自建子发电机。
因为它本身就是生成器,所以执行 body
直到next(generator)的第一次调用才开始。
def gen_generator(func, seq):
    """Generator yielding from sub-generator inside."""
    # do stuff ... first time when 'next' gets called
    print("build generator & yield")
    yield from (func(*args) for args in seq)

我认为以下示例将使其更加清晰。
我们定义了要使用功能处理的数据包,
bundle 在工作,我们传递给发电机。
def add(a, b):
    return a + b

def sqrt(a):
    return a ** 0.5

data1 = [*zip(range(1, 5))]  # [(1,), (2,), (3,), (4,)]
data2 = [(2, 1), (3, 1), (4, 1), (5, 1)]

job1 = (sqrt, data1)
job2 = (add, data2)

现在,我们在像IPython这样的交互式shell中运行以下代码,以
看到不同的行为。 gen_factory立即打印
出来,而gen_generator仅在调用next()之后才这样做。
gen_fac = gen_factory(*job1)
# build generator & return <-- printed immediately
next(gen_fac)  # start
# Out: 1.0
[*gen_fac]  # deplete rest of generator
# Out: [1.4142135623730951, 1.7320508075688772, 2.0]

gen_gen = gen_generator(*job1)
next(gen_gen)  # start
# build generator & yield <-- printed with first next()
# Out: 1.0
[*gen_gen]  # deplete rest of generator
# Out: [1.4142135623730951, 1.7320508075688772, 2.0]

为您提供一个更合理的构造用例示例
gen_generator一样,我们将其扩展一点并制作一个协程
通过将 yield 分配给变量来解决这个问题,所以我们可以注入(inject)工作
使用send()进入运行的生成器。

此外,我们创建了一个辅助函数,它将运行所有任务
内的工作,并在完成后要求新的。
def gen_coroutine():
    """Generator coroutine yielding from sub-generator inside."""
    # do stuff... first time when 'next' gets called
    print("receive job, build generator & yield, loop")
    while True:
        try:
            func, seq = yield "send me work ... or I quit with next next()"
        except TypeError:
            return "no job left"
        else:
            yield from (func(*args) for args in seq)


def do_job(gen, job):
    """Run all tasks in job."""
    print(gen.send(job))
    while True:
        result = next(gen)
        print(result)
        if result == "send me work ... or I quit with next next()":
            break

现在,我们使用辅助函数gen_coroutine和两个作业运行do_job
gen_co = gen_coroutine()
next(gen_co)  # start
# receive job, build generator & yield, loop  <-- printed with first next()
# Out:'send me work ... or I quit with next next()'
do_job(gen_co, job1)  # prints out all results from job
# 1
# 1.4142135623730951
# 1.7320508075688772
# 2.0
# send me work... or I quit with next next()
do_job(gen_co, job2)  # send another job into generator
# 3
# 4
# 5
# 6
# send me work... or I quit with next next()
next(gen_co)
# Traceback ...
# StopIteration: no job left

回到您的问题,通常哪个版本是更好的方法。
IMO诸如gen_factory之类的东西只有在您需要为要创建的多个生成器完成相同的事情时才有意义,或者在您的生成器构建过程非常复杂以至于证明使用工厂而不是用生成器在原地构建单个生成器时才有意义。理解。

笔记:

上面关于gen_generator函数(第二个mymap)状态的描述
“它本身就是一个发电机”。这有点含糊,从技术上讲不是
确实正确,但有助于对功能差异进行推理
在这个棘手的设置中,gen_factory也返回一个生成器,即
其中一台由发电机理解而建。

实际上任何函数(不仅是这个问题的函数,里面有生成器理解!),里面有yield,在调用时,
返回一个生成器对象,该对象从函数主体中构造出来。
type(gen_coroutine) # functiongen_co = gen_coroutine(); type(gen_co) # generator
因此,我们在上面观察到的gen_generatorgen_coroutine的整个操作
发生在这些生成器对象中,之前带有yield的函数已经吐出。

关于python - Python `yield from`,还是返回一个生成器?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/41136410/

10-09 17:27
查看更多