gpt4 book ai didi

Python `yield from` ,还是返回一个生成器?

转载 作者:IT老高 更新时间:2023-10-28 20:48:41 27 4
gpt4 key购买 nike

我写了这段简单的代码:

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))

除了“return”和“yield from”之间的技术差异之外,一般情况下哪种方法更好?

最佳答案

不同的是,你的第一个 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也是一个工厂,但它也是一个生成器本身,由内部自建的子发电机产生。因为它本身就是一个生成器,所以主体的执行确实直到第一次调用 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)

我认为下面的例子会更清楚。我们定义了应该用函数处理的数据包,捆绑在我们传递给生成器的作业中。

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 我们将对其进行一些扩展并制作一个协程通过将产量分配给变量来摆脱它,所以我们可以注入(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

现在我们使用辅助函数 do_job 和两个作业运行 gen_coroutine

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 还返回一个生成器,即由里面的生成器理解构建的。

事实上 any 函数(不仅是这个问题中带有生成器理解的函数!)在调用时,内部带有 yield返回一个从函数体构造出来的生成器对象。

type(gen_coroutine) # 函数
gen_co = gen_coroutine(); type(gen_co) # 生成器

所以我们在上面观察到的 gen_generatorgen_coroutine 的整个 Action 发生在这些生成器对象中,内部带有 yield 的函数之前已经吐出。

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

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com