Python方法也是一个生成器函数?

时间:2011-03-25 12:00:06

标签: python generator

我正在尝试构建一个方法,它也像一个生成器函数,只需一个开关(want_gen下方)。

类似的东西:

def optimize(x, want_gen):
    # ... declaration and validation code
    for i in range(100):
        # estimate foo, bar, baz
        # ... some code here

        x = calculate_next_x(x, foo, bar, baz)

        if want_gen:
           yield x

    if not want_gen:
       return x

但当然这不起作用 - Python显然不允许在同一方法中使用yieldreturn,即使它们不能同时执行。

代码非常复杂,重构声明和验证代码没有多大意义(太多的状态变量 - 我最终会得到7+参数的难以命名的辅助例程,这显然是丑陋的)。当然,我希望尽可能避免代码重复。

是否有一些代码模式可以在这里实现我想要的行为?


为什么我需要它?

我有一个相当复杂和耗时的优化例程,我希望在运行时获得有关其当前状态的反馈(以便在例如GUI中显示)。旧的行为需要存在以实现向后兼容。多线程和消息传递工作太多,只需要太少的额外好处,特别是在需要跨平台操作时。

修改 也许我应该提到,因为每个优化步骤都相当冗长(也涉及一些数值模拟),我希望能够在某个迭代中“介入”并旋转一些参数,或者中止整个业务共。发电机似乎是一个好主意,因为我可以自行决定启动另一次迭代,同时摆弄一些参数。

7 个答案:

答案 0 :(得分:3)

由于您似乎想要的是对长时间运行的函数的某种反馈,为什么不直接传入对将定期调用的回调过程的引用?

答案 1 :(得分:2)

编辑我的答案,为什么不总是屈服?您可以拥有一个产生单个值的函数。如果您不想这样,那么只需选择让您的函数返回生成器本身或值:

 def stuff(x, want_gen):
     if want_gen:
         def my_gen(x):
             #code with yield
         return my_gen
     else:
         return x

这样你总是会返回一个值。在Python中,函数对象。

答案 2 :(得分:2)

嗯......我们总能记住,在语言中实现了yield是为了促进生成器对象的存在,但是人们总是可以从头开始实现它们,或者充分利用这两个世界:

class  Optimize(object):
    def __init__(self, x):
        self.x = x
    def __iter__(self):
        x = self.x
        # ... declaration and validation code
        for i in range(100):
            # estimate foo, bar, baz
            # ... some code here

            x = calculate_next_x(x, foo, bar, baz)
            yield x
    def __call__(self):
        gen = iter(self)
        return gen.next()

def optimize(x, wantgen):
    if wantgen:
        return iter(Optimize(x))
    else:
        return Optimize(x)()

并不是说你甚至不需要“优化”函数包装器 - 我只是把它放在那里,所以它成为你的例子的替代品(它会起作用)。

声明类的方式,你可以做到:

for y in Optimize(x):
    #code

将其用作生成器,或者:

 k = Optimize(x)()

将其用作功能。

答案 3 :(得分:1)

如果你在发电机和功能模式之间切换,你还不清楚你想要发生什么。

但是作为第一次尝试:或许将生成器版本包装在一个明确抛弃中间步骤的新方法中?

def gen():
    for i in range(100):
        yield i

def wrap():
    for x in gen():
        pass
    return x

print "wrap=", wrap()

使用此版本,您可以通过循环显示较小数量的范围进行gen(),进行调整,然后仅在您想要完成时使用wrap()

答案 4 :(得分:1)

有点乱,但我认为这跟你原来的代码要求一样:

def optimize(x, want_gen):
    def optimize_gen(x):
        # ... declaration and validation code
        for i in range(100):
            # estimate foo, bar, baz
            # ... some code here

            x = calculate_next_x(x, foo, bar, baz)

            if want_gen:
               yield x
    if want_gen:
        return optimize_gen(x)

    for x in optimize_gen(x):
        pass
    return x

或者,最后的for循环可以写成:

    return list(optimize_gen(x))[-1]

现在问问自己是否真的想要这样做。你为什么有时想要整个序列,有时只想要最后一个元素?闻起来有点腥。

答案 5 :(得分:0)

最简单的方法是编写两个方法,一个是生成器,另一个是调用生成器,只返回值。如果你真的想要一个具有两种可能性的函数,你总是可以使用want_gen标志来测试返回值的类型,返回生成器函数在生成函数时生成的迭代器,否则返回值。

答案 6 :(得分:0)

这种模式怎么样?进行3行更改以将函数转换为生成器。将其重命名为NewFunctionName。将现有函数替换为如果want_gen为True则返回生成器,或者耗尽生成器并返回最终值。