在另一个函数中调用生成器的yield [英] invoking yield for a generator in another function

查看:126
本文介绍了在另一个函数中调用生成器的yield的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有一些经理对象。这个对象的API有一个 main_hook 函数,它获得另一个函数 f 作为参数,并运行给定的<$ c
$ b

  def main_hook($ c $ f $ / code>)在循环中做一些事情: self,f):
while(self.shouldContinue()):
#做一些准备工作
f(self)
#一些拆卸

现在,我也有(更准确地说,想拥有)函数 stop_and_do_stuff ,一旦被调用,停止 main_hook 死在它的轨道中,将控制权返回给任何称为 main_hook ,之后func完成了它所做的事情,将控制权交还给main_hook并继续。基本上,结果将与

  def main_hook(self,f):
while(self.shouldContinue ()):
#做一些准备工作
yield
#一些拆卸

除了 yield 之外,我希望调用 f(),同时给出 f 选择调用 self.stop_and_do_stuff()



I 无法通过使f也是一个生成器来解决这个问题,原因有两个:

1。 f 不是我的API的一部分 - 它是使用我的lib的用户给我的



2.即使可以要求他使用yield,放置在他需要调用 stop_and_do_stuff 的代码中不会直接放在f中,而是放在函数堆栈中的某个地方,放在 f(),但不直接在其中,例如

  def h(manager):
#do stuff
如果应该停止:
manager.stop_and_do_stuff()
#更多东西
def g(经理):
#some stuff
如果应该停止:
manager.stop_and_do_stuff()
#更多东西
如果应该再次停止:
manager.stop_and_do_stuff()
如果应该调用h:
h()
def f(manager):
g(经理)

所以如果我选择使 f 一个生成器,我还需要使 g 生成器以及 h ,否则这个技巧不会工作。

有没有解决所有这些问题的方法?也许我试图以错误的方式解决它?



(我知道这个问题很长很丑 - 这是我能做的最好的事情。不清楚请告诉我,我会澄清它)

a href =http://www.python.org/dev/peps/pep-0342/ =noreferrer> pep 342 是否是解决方案?

解决方案

我相信我也应该从另一个角度增加一个答案,也就是不要试图解释你如何才能实现我们能够理解你正在做的事情,但是为什么 yield 肯定无法工作。



当函数包含 yield 关键字被深度修改。它仍然是一个可调用但不是正常的函数:它变成了一个返回迭代器的工厂。



从调用者的角度来看,三者之间没有区别(除了 yield 其中一个简单得多)。

  ########################################## 
printFunction (),
$ b def gen():
(x,
尝试:
,而True:
打印f.next(),
除了StopIteration:
通过

为gen in :
print x,

print

######################### ################
printClass iterator iter and next,

class gen2(object):

def __init __(self):
self.index = 0;
self.limit = 10;

def __iter __(self):
return self

def next(self):
如果self.index> = self.limit:
提高StopIteration
self.index + = 1;
返回self.index - 1;


f = gen2()
尝试:
而真:
打印f.next(),
除了StopIteration:
在gen2()中为x传递


print x,
print

############# ############################
print使用iter()和sentinel的函数迭代器,
def gen3 ():
def g3():
如果g3.index是None:
g3.index = 0
g3.index + = 1;
return g3.index - 1

g3.index = None
return iter(g3,10)

f = gen3()
尝试:
,而True:
打印f.next(),
,除了StopIteration:
通过

为gen3()中的x:
print x,
print

然后您应该明白,收益并不是很多关于控制流,而是关于在变量内部保持调用上下文。一旦明白你必须决定main_loop的API是否真的想为它的调用者提供一个迭代器。那么如果是这样,如果f可能循环它必须也应该是一个迭代器(并且应该有一个围绕调用f()的循环,如下所示)。

  def main_hook(self,f):
while(self.shouldContinue()):
#做一些准备
for v in(self):
yield v
#做一些拆卸

但是你不应该在乎f()是否需要调用内部函数g()等。这完全不相关。你提供了一个lib,这是你的用户问题与适当的迭代调用。如果您认为您的lib用户无法做到,您将不得不改变整体设计。



希望它有帮助。


suppose I have some manager object. This object's API has a main_hook function, that gets another function f as it's argument, and runs the given f in a loop, doing some stuff in between each iteration:

def main_hook(self,f):
    while (self.shouldContinue()):
        #do some preparations
        f(self)
        #do some tear down

Now, I also have (more accurately, would like to have) a function stop_and_do_stuff, that once called, stops main_hook dead in it's tracks, returns the control to whichever func called main_hook, and after that func finished what's it doing, get control back to main_hook and continue. Basically the result will be the same as doing

def main_hook(self,f):
    while (self.shouldContinue()):
        #do some preparations
        yield
        #do some tear down

Except that instead yield I want to have a call to f(), while giving f the option to call self.stop_and_do_stuff()

I can't work around this by making f also a generator for 2 reasons:

1.f isn't part of my API - it's given to me by a user who uses my lib

2.Even if could ask him to use yield, the place in the code in which he will need to call stop_and_do_stuff won't be directly inside f, rather in some place in the function stack which will be inside f(), but not directly in it, e.g

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)

so if I choose to make f a generator, I also need to make g a generator and also h, otherwise this trick won't work.

Is there any solution to all of this? maybe I'm trying to solve it the wrong way?

(I know this question is long and ugly - it's the best I could do. If something isn't clear please tell me and I'll clarify it)

EDIT

Maybe pep 342 is the solution?

解决方案

I believe I should also add an answer from the other point of view, ie not trying to explain how you could achieve what we can understand of what you are trying to do, but why yield definitely couldn't possibly work.

When a function contains yield keyword it is deeply modified. It is still a callable but not a normal function any more : it becomes a factory that return an iterator.

From the caller's point of view there is no difference between the three implementations below (except that the yield one is so much simpler).

##########################################
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

Then you should understand that yield is not much about control flow, but about keeping call context inside variables. Once it is understood you have to decide if the API of main_loop really want to provide an iterator to it's caller. Then if so, if f may loop it must should also be an iterator (and there should be a loop around calls to f() like below).

def main_hook(self,f):
    while (self.shouldContinue()):
        #do some preparations
        for v in f(self):
            yield v
        #do some tear down

But you should not care if f() has to call inner functions g(), etc. That is completely irrelevant. You provide a lib and it is your user problem to call with an appropriate iterable. If you believe your lib user won't be able to, you will have to change the overall design.

Hope it helps.

这篇关于在另一个函数中调用生成器的yield的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆