Python方法也可以是生成器函数吗?
我正在尝试创建一个方法,它可以像生成器函数一样工作,只需简单地切换一个开关(下面的 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个,这样看起来很糟糕)。当然,我希望尽量避免代码重复。
有没有什么代码模式可以让我实现我想要的行为呢?
我为什么需要这个?
我有一个相当复杂且耗时的优化过程,我希望在运行时能够获得关于其当前状态的反馈(比如在图形界面上显示)。旧的行为需要保留,以便与之前的版本兼容。多线程和消息传递的工作量太大,带来的好处却不多,尤其是在需要跨平台操作的时候。
编辑:也许我应该提到,由于每个优化步骤都比较耗时(还涉及一些数值模拟),我希望能够在某个迭代步骤中“插入”并调整一些参数,或者干脆中止整个过程。使用生成器似乎是个好主意,因为我可以在需要的时候启动另一个迭代,同时调整一些参数。
7 个回答
好吧……我们可以记住,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)()
把它当作普通函数使用。
我对我的回答做了个补充,为什么不总是使用“yield”呢?你可以写一个只返回一个值的函数。如果你不想这样,那就可以选择让你的函数返回一个生成器,或者直接返回一个值:
def stuff(x, want_gen):
if want_gen:
def my_gen(x):
#code with yield
return my_gen
else:
return x
这样你就总是能返回一个值。在Python中,函数本身就是对象。
既然你只是想要对一个运行很久的函数得到一些反馈,那为什么不传入一个回调函数的引用呢?这样可以在固定的时间间隔内调用这个回调函数,给你一些进度更新。