不同的是你的第一个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 我们将对其进行一些扩展并制作一个协程
通过将产量分配给变量来摆脱它,所以我们可以注入工作
使用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) # function
gen_co = gen_coroutine(); type(gen_co) # generator
所以我们在上面观察到的gen_generator 和gen_coroutine 的整个动作
发生在这些生成器对象中,内部带有yield 的函数之前已经吐出。