您好, 欢迎来到 !    登录 | 注册 | | 设为首页 | 收藏本站

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

Python`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的交互式外壳中运行以下代码,以查看不同的行为。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) # functiongen_co = gen_coroutine(); type(gen_co) # generator

因此,我们在上面观察到的gen_generatorgen_coroutine 在这生成器对象中进行的整个动作yield之前,带有内部功能函数已经吐出。

python 2022/1/1 18:37:54 有240人围观

撰写回答


你尚未登录,登录后可以

和开发者交流问题的细节

关注并接收问题和回答的更新提醒

参与内容的编辑和改进,让解决方法与时俱进

请先登录

推荐问题


联系我
置顶