2014-02-18 33 views
1

我认为这是一个伟大的时刻使用yield,但我卡住了。将项目添加到可迭代(收益/生成器)

当某件事情失败时,我想将该物品寄回发电机。我读过这是可能的,所以我真的很想使用我的第一台发电机。

states = ["IL", "NY", "NJ"] 
for state in states: 
    ok = do something 
    if not ok: 
     *add state back as the first-to-deal with in the generator* 

如何在这种情况下使用发电机?

+0

这看起来像一个无限循环给我。你可以把它放到第二个列表中“稍后处理”,否则你将继续处理同一个项目并将它放回列表中? – Cuadue

+0

它有一个外部依赖(基于互联网),所以当有连接它会继续,我会添加一些计时器,然后允许无限运行(以为它不会)。 – PascalVKooten

回答

2

您可能指的是coroutine,它利用产量表达式。它的工作原理有点像这样:

def co_gen(li): 
    for x in li: 
     bad = yield x 
     if bad is not None: 
      print('ack! {}'.format(bad)) 
      #other error handling 

和(人为)用法:

states = ["IL", "NY", "NJ"] 

gen = co_gen(states) 

for x in gen: 
    print('processing state: {}'.format(x)) 
    if x == 'NY': 
     y = gen.send('Boo, Yankees!') 
     print('processing state after error: {}'.format(y)) 

# processing state: IL 
# processing state: NY 
# ack! Boo, Yankees! 
# processing state after error: NJ 

要点 - 正常yield行为分配Nonebad。如果它不是None,那么已将send装入生成器。

当我们send东西进入发生器,它恢复运行,直到它达到下一个产量表达。所以记住这一点 - 协程中的上述控制流程并不是我所称的“标准”,因为在错误块中没有完成yield

这里是工作有点更像是你所讲的协同程序:

def co_gen(li): 
    for x in li: 
     bad = yield x 
     while bad is not None: 
      print('error in generator: {}'.format(bad)) 
      yield 
      bad = yield bad 

gen = co_gen(states) 

for x in gen: 
    print('processing state: {}'.format(x)) 
    if random.choice([0,1]): 
     gen.send(x) #discard first yield 
     print('error: trying {} again'.format(x)) 

# processing state: IL 
# error in generator: IL 
# error: trying IL again 
# processing state: IL 
# processing state: NY 
# error in generator: NY 
# error: trying NY again 
# processing state: NY 
# processing state: NJ 

我们send我们的国家回到发生器,它不断地产生,直到我们停止发送。

+0

由于'NJ'在第一个'NY'(导致错误)之后返回,所以不会解决'将状态添加回作为生成器中的第一个处理'的要求。 – isedev

+0

@isedev是的,你可以把'yield's放入'#error handling'块,就像我说的。我会更加明确。 – roippi

+0

它确实做了我所希望的事情,但它确实看起来比我想象的要复杂得多...... – PascalVKooten

0
def ok(i): 
    from random import randint 
    return bool(randint(0,1)) 


def mygen(iterable): 
    def helper(iterable): 
     for i in iterable: 
      elem = yield i 
      if elem: 
       iterable.append(elem) 

    it = helper(iterable) 
    sendBack = False 
    while True: 
     try: 
      if sendBack: 
       print "Sending back {0}".format(i) 
       i = it.send(i) 
      else: 
       i = it.send(None) 
      if ok(i): 
       sendBack = False 
       yield i 
      else: 
       sendBack = True 

     except StopIteration: 
      break 

x = range(10) 

print list(mygen(x)) 
#Sending back 1 
#Sending back 5 
#Sending back 7 
#Sending back 1 
#Sending back 7 
#[0, 2, 3, 4, 6, 8, 9, 5, 1, 7] 

两个生成器,如果某些函数的返回值计算结果为false,则返回值。 (在这种情况下是随机的)。

+0

它应该放在前面,但没有到最后? – PascalVKooten

0

尽管可以使用普通生成器和gen.send()sent_back = yield x来做什么,但代码会非常复杂。这可能是更容易编写你自己的iterator type,它支持比send为让“发回”项目之外的其他方法:

class SendBackIter(object): 
    def __init__(self, iterable): 
     self.iterator = iter(iterable) 
     self.sent_back = [] 

    def __iter__(self): 
     return self 

    def __next__(self): 
     if self.sent_back:    # if the stack is not empty... 
      return self.sent_back.pop() # return the last item from the sent_back stack 
     return next(self.iterator)  # otherwise return an item from our iterator 

    def send_back(self, obj): 
     self.sent_back.append(obj) 

如果你只需要处理重复刚出来迭代的项目,你可以把它简单又:

def RepeatableIter(object); 
    def __init__(self, iterable): 
     self.iterator = iter(iterable) 
     self.last_item = None 
     self.repeat = False # client code can set this to True to repeat the last value 

    def __iter__(self): 
     return self 

    def __next__(self): 
     if self.repeat: 
      self.repeat = False # only repeat once, by default 
     else: 
      self.last_item = next(self.iterator) 

     return self.last_item 

这里是你如何利用这最后的版本:

it = RepeatableIter(["foo", "bar", "baz"]) 
for item in it: 
    if is_not_ok(item): 
     it.repeat = True # this means we will get the same item on the next iteration 
    else: 
     do_something(item)