ola*_*ndo 9 python yield function generator
假设我有一些经理对象.这个对象的API有一个main_hook函数,它获取另一个函数f作为它的参数,并f在循环中运行给定的东西,在每次迭代之间做一些事情:
def main_hook(self,f):
while (self.shouldContinue()):
#do some preparations
f(self)
#do some tear down
Run Code Online (Sandbox Code Playgroud)
现在,我也有(更确切地说,想要)一个函数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
Run Code Online (Sandbox Code Playgroud)
除此之外,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)
Run Code Online (Sandbox Code Playgroud)
因此,如果我选择制造f发电机,我还需要制造g发电机h,否则这个技巧将不起作用.
这有什么解决方案吗?也许我试图以错误的方式解决它?
(我知道这个问题很长很难看 - 这是我能做的最好的事情.如果事情不清楚,请告诉我,我会澄清一下)
也许pep 342是解决方案?
我之前的回答描述了如何在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)
Run Code Online (Sandbox Code Playgroud)
结果是:
generator yields 0
generator yields 1
generator yields 2
returned 3
generator yields 4
Run Code Online (Sandbox Code Playgroud)
异常会按照您期望的方式传递。
我相信我还应该从另一个角度添加一个答案,即不要试图解释你如何实现我们可以理解你想要做的事情,但为什么肯定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
Run Code Online (Sandbox Code Playgroud)
那么你应该明白,yield 与控制流无关,而是将调用上下文保留在变量中。一旦理解了这一点,您就必须决定 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
Run Code Online (Sandbox Code Playgroud)
但是你不应该关心 f() 是否必须调用内部函数 g() 等。这是完全不相关的。您提供了一个库,并且使用适当的可迭代对象进行调用是您的用户问题。如果您认为您的库用户无法做到这一点,您将不得不更改整体设计。
希望能帮助到你。
| 归档时间: |
|
| 查看次数: |
5137 次 |
| 最近记录: |