假设我有一些经理对象。这个对象的API有一个main_hook
函数,它获取另一个函数f
作为它的参数,并在循环中运行给定的f
,在每次迭代之间做一些事情:
def main_hook(self,f):
while (self.shouldContinue()):
#do some preparations
f(self)
#do some tear down
现在,我也(更准确地说,想要)一个函数stop_and_do_stuff
,一旦调用,就会停止main_hook
在其轨道中死亡,返回控件对于那个名为main_hook
的函数,在那个函数完成它正在做的事情之后,控制回到main_hook并继续。基本上结果与执行
def main_hook(self,f):
while (self.shouldContinue()):
#do some preparations
yield
#do some tear down
取而代之的是yield
,我想打电话给f()
,同时给f
选项以self.stop_and_do_stuff()
我不能通过使f也成为生成器来解决这个问题有两个原因:
1。f
不是我的API的一部分 - 它是由使用我的lib的用户提供给我的
2.即使可以要求他使用yield,他需要调用stop_and_do_stuff
的代码中的位置也不会直接在f内,而是在函数堆栈中的某个位置将是在f()
内,但不直接在其中,例如
def h(manager):
#do stuff
if should stop:
manager.stop_and_do_stuff()
#do more stuff
def g(manager):
#some stuff
if should stop:
manager.stop_and_do_stuff()
#more stuff
if should stop again:
manager.stop_and_do_stuff()
if should call h:
h()
def f(manager):
g(manager)
因此,如果我选择将f
设为生成器,我还需要将g
设为生成器,并h
,否则此技巧将无效。
这有什么解决办法吗?也许我试图以错误的方式解决它?
(我知道这个问题很长很丑 - 这是我能做的最好的。如果有什么不清楚,请告诉我,我会澄清它)
也许pep 342是解决方案?
答案 0 :(得分:3)
我之前的回答描述了如何在Python2中执行此操作,这非常难看。但现在我遇到了PEP 380:委托给子生成器的语法。这完全符合你的要求。唯一的问题是它需要Python3。但这不应该是一个问题。
以下是它的工作原理:
def worker():
yield 1
yield 2
return 3
def main():
yield 0
value = yield from worker()
print('returned %d' % value)
yield 4
for m in main():
print('generator yields %d' % m)
结果是:
generator yields 0
generator yields 1
generator yields 2
returned 3
generator yields 4
例外情况会以您期望的方式传递。
答案 1 :(得分:2)
我相信我也应该从另一个角度添加一个答案,即不试图解释如何实现我们能够理解你想要做什么,但为什么yield
绝对不能可能会工作。
当某个函数包含yield
个关键字时,会对其进行深度修改。它仍然是一个可调用但不再是正常函数:它成为一个返回迭代器的工厂。
从调用者的角度来看,下面的三个实现之间没有区别(除了yield
之外这么简单)。
##########################################
print "Function iterator using yield",
def gen():
for x in range(0, 10):
yield x
f = gen()
try:
while True:
print f.next(),
except StopIteration:
pass
for x in gen():
print x,
print
#########################################
print "Class iterator defining iter and next",
class gen2(object):
def __init__(self):
self.index = 0;
self.limit = 10;
def __iter__(self):
return self
def next(self):
if self.index >= self.limit:
raise StopIteration
self.index += 1;
return self.index - 1;
f = gen2()
try:
while True:
print f.next(),
except StopIteration:
pass
for x in gen2():
print x,
print
#########################################
print "Function iterator using iter() and sentinel",
def gen3():
def g3():
if g3.index is None:
g3.index = 0
g3.index += 1;
return g3.index - 1
g3.index = None
return iter(g3, 10)
f = gen3()
try:
while True:
print f.next(),
except StopIteration:
pass
for x in gen3():
print x,
print
然后你应该明白,控制流量并不是很大,而是将调用上下文保留在变量中。一旦理解,你必须决定main_loop的API是否真的想为它的调用者提供一个迭代器。然后,如果是这样,如果f可以循环,它必须也应该是一个迭代器(并且应该有一个循环调用f(),如下所示)。
def main_hook(self,f):
while (self.shouldContinue()):
#do some preparations
for v in f(self):
yield v
#do some tear down
但你不应该关心f()是否必须调用内部函数g()等。这完全无关紧要。您提供了一个lib,使用适当的iterable调用是您的用户问题。如果您认为您的lib用户将无法使用,则必须更改整体设计。
希望它有所帮助。
答案 2 :(得分:1)
我也不了解整个(main_hook调用者看起来是什么样的?),但我会说,当你应该停止时抛出一个StopNow异常,就像你应该在你的生成器完成时抛出StopIteration一样。 / p>
这是我如何理解这件事以及我会做什么。
class StopNow(Exception):
pass
def main_hook(self,f):
got_stop_now_exc = False
while (!got_stop_now_exc and self.shouldContinue()):
#do some preparations
try:
f(self)
except StopNow:
got_stop_now_exc = True
#do some compulsary tear down, exception or not
def stop_and_do_stuff()
raise StopNow()
def my_f():
if needed:
stop_and_do_stuff()
def the_main_hook_caller():
while i_should:
managerthingie.main_hook(my_f)
do_stuff()
答案 3 :(得分:0)
我不太清楚你究竟想要实现什么,所以如果你可以更多地解释问题而不是提供更好的解决方案。
从我的部分理解为什么你不做这样的事情
def main_hook(self,f):
while (self.shouldContinue()):
#do some preparations
stop_and_do_stuff = f(self)
if stop_and_do_stuff :
yield
#do some tear down
所以基本上f
会返回一个停止或不停止的标志,如果它表示停止,我们会调用main_hook函数,并且该函数可以在执行某些操作后继续执行
e.g。
class A(object):
def main_hook(self,f):
while (self.shouldContinue()):
#do some preparations
stop = f(self)
if stop:
yield
#do some tear down
def shouldContinue(self):
return True
def f(a):
return True
a = A()
for x in a.main_hook(f):
print x
答案 4 :(得分:0)
您描述的行为看起来就像一个简单的函数调用。如下所示。
def f(manager):
print("Entering f")
manager.stop_and_do_stuff()
print("Exiting f")
class Manager(Object):
def shouldContinue(self):
return True
def stop_and_do_stuff(self):
print("Manager stop and do stuff")
def main_hook(self,f):
while self.shouldContinue()
print("Manager Setup")
f(self)
print("Manager Tear Down")
如果另一个用户提供了f()
,如果从某个内部函数调用了stop_and_do_stuff,则没问题。如果您还希望管理器能够从stop_and_do_stuff
展开堆栈并在某些情况下真正退出,则没问题。只是从中引出一些异常,你就可以从main_hook或高级代码中捕获它。
你应该可以从stop_and_and_do_stuff()
内部做任何你想做的事情来自主钩的调用者。如果没有,你应该解释原因。
问题中不清楚的是main_hook()的调用方发生了什么,以及为什么你希望能够退出main_hook循环,但实际上并不是这样。 main_loop调用者要求生成器要么不要求生成器。如果你想得到一个明智的答案,你需要解释那个部分(一些上下文信息也会很好,如果你真的解释你试图做的WTF,你真正的限制 - 你说f是由其他一些用户和main_hook提供的在lib中,main_hook调用者是什么? - 可能是众所周知的常用解决方案。)