理解Python中嵌套的yield/return

23

我有一个 Python 函数,其输出是一个生成器:

def main_function(x):
    r = get_range()
    for i in range(r):
        yield x+i

我想重构这段代码(我已经简化了使用情况,但实际的计算可能会更复杂且更长。请参见下面的EDIT)。根据我的理解,为了保持功能不变,我应该做以下几点:

(a) 与原始代码具有相同的接口

    def sub_function(x,r):
        for i in range(r):
            yield x+i    

    def main_function(x):
        r = get_range()
        return sub_function(x,r)

与其他方法相比:

(b) 这会返回一个生成器的生成器(这种方法有什么优势吗?)

    def sub_function(x,r):
        for i in range(r):
            yield x+i    

    def main_function(x):
        r = get_range()
        yield sub_function(x,r)

(c) 这将违背生成器的设计初衷(这样说对吗?)

    def sub_function(x,r):
        return [x+i for i in range(r)]

    def main_function(x):
        r = get_range()
        for i in sub_function(x,r):
            yield(i)

编辑: 评论指出正确答案取决于使用情况。我想补充说明,我的使用情况是解析XML文件以提取字段并将它们写入数据库。这部分被委托给子函数(sub_function())。我也问了这个问题,以便更好地理解嵌套yield用于重构代码的用法。


不幸的是,实际答案将取决于这种简化和您实际使用情况之间的差异。如果真正的“main_function”需要执行任何重要操作,则可能被迫修改生成的序列... - Karl Knechtel
没有具体的使用案例,就不可能给出明确的答案。使用最适合您当前需求的方法。 - Duncan
1个回答

13

你说得对,初始示例和a)做了相同的事情,因为两者都返回生成器。

b)不同: 它返回一个生成器,该生成器产生单个元素(另一个生成器)。 要使用它,您需要两个循环(一个在外部生成器上,一个在内部生成器上)。

本质上没有优势,但有时构建嵌套生成器可能会很有用。

c)可能更糟糕,但我非常确定[ x for x in y ]实际上也是一个生成器。 因此,它的成本会更高一些,但并不是那么高。


你针对这个简单的情况,对生成器和列表推导式进行了性能分析吗? - Paulo Scardine
5
至少在2.x版本中:[x for x in y] 会创建一个列表。(x for x in y)会创建一个生成器(同时不会改变或创建locals()中的 x)。 - Karl Knechtel
3
我曾经进行过比较,将list((x for x in l))[x for x in l]进行了对比。发现在 Python 2 中,前者(1.96 毫秒)比后者(1.44 毫秒)慢(对于一个包含 10000 个项目的列表,使用 python -mtimeit "l=range(10000)" "list( (x for x in l) )"python -mtimeit "l=range(10000)" "[x for x in l]")。但是,在 Python 3 中,第二种方法应该与第一种方法相同,因为他们移除了 Python 2 中列表推导式所使用的技巧,并将第二种方法变成了第一种。 - Dan D.
2
我知道这个答案已经超过11年了。但是,至少在Python 3中,替代方案“a”不应该使用return而是使用yield from才能达到等效吗?(或者替代地,使用for i in sub_function(x, r): yield i。) - Arjan
@Arjan 我不知道。我从未使用过 yield from,而且对 Python 3 的时间也不多。 - Aaron Digulla

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接