不同之处在于您的第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的交互式外壳中运行以下代码,以查看不同的行为。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分配给变量来从协程中生成协程,从而可以使用来将作业注入正在运行的生成器中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
只有在您需要为多个要创建的发电机完成相同的工作时才有意义,或者在您的发电机构造过程非常复杂以至于需要使用工厂而不是借助发电机理解来就地建造单个发电机的情况下,IMO才有意义。 。
上面对gen_generator
函数(第二个mymap
)的描述指出“它本身 就是 一个生成器”。这有点含糊,并且在技术上并不真正正确,但是有助于在这种棘手的设置中推断功能的差异,在这种棘手的设置中gen_factory
还返回了一个生成器,即内部由生成器理解的那个生成器。
实际上, 具有内部功能的函数(不仅是带有内部生成器理解的问题!)yield
,在调用时,仅 返回 生成器对象,该对象从函数主体中构造出来。
type(gen_coroutine) # function
gen_co = gen_coroutine(); type(gen_co) # generator
因此,我们在上面观察到的gen_generator
并gen_coroutine
在这些生成器对象中进行的整个动作yield
之前,带有内部功能的函数已经吐出。