我正在尝试构建一个方法,它也像一个生成器函数,只需一个开关(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显然不允许在同一方法中使用yield
和return
,即使它们不能同时执行。
代码非常复杂,重构声明和验证代码没有多大意义(太多的状态变量 - 我最终会得到7+参数的难以命名的辅助例程,这显然是丑陋的)。当然,我希望尽可能避免代码重复。
是否有一些代码模式可以在这里实现我想要的行为?
为什么我需要它?
我有一个相当复杂和耗时的优化例程,我希望在运行时获得有关其当前状态的反馈(以便在例如GUI中显示)。旧的行为需要存在以实现向后兼容。多线程和消息传递工作太多,只需要太少的额外好处,特别是在需要跨平台操作时。
修改 也许我应该提到,因为每个优化步骤都相当冗长(也涉及一些数值模拟),我希望能够在某个迭代中“介入”并旋转一些参数,或者中止整个业务共。发电机似乎是一个好主意,因为我可以自行决定启动另一次迭代,同时摆弄一些参数。
答案 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则返回生成器,或者耗尽生成器并返回最终值。