2015-12-03 24 views
1

比方說,我有一個這樣的發電機:從有條件使用Python中產量

def a(data): 
    for val in data: 
     yield val 

而且我們說,我想在另一個發電機,b來包裝這個發電機,只產生一些值從a ,這取決於它們的價值。 b應該能夠將從調用者發回的值轉發至a。我知道在另一個生成器中包裝發電機的最新方法是使用yield from聲明。喜歡的東西:

def b(data): 
    yield from val = a(data) if val == "foo" 

我知道,語法錯誤(它只是在整個想法得到),所以我不知道是否有做yield from工作,條件語句的正確道路。還是有一些其他的構想我應該使用?

+0

在你的例子中,'a'只產生一個單一的值。這就是你真正想問的問題,或者你是在想象一個「a」產生很多價值的情況? – BrenBarn

+0

你是對的 - 例子已更新。 – LateCoder

回答

1

正如你所說,yield from只適用於你想從包裹的發電機通過所有的情況。如果你不希望出現這種情況,您需要手動遍歷包發生器,做你想要什麼:

def b(data): 
    for value in a(data): 
     if some_condition(value): 
      yield value 
     # otherwise don't yield it. 

如果你想處理send,你需要處理的是自己太。下面是一個簡單的例子,你可以玩的,看看發生了什麼事情:

def a(): 
    sent = yield "Begin" 
    for x in [1, 2, 3]: 
     if sent: 
      sent = yield "You sent {0}".format(sent) 
     else: 
      sent = yield x 

def b(): 
    gen = a() 
    val = yield next(gen) 
    while True: 
     wrappedVal = gen.send(val) 
     if wrappedVal == 2: 
      continue 
     val = yield wrappedVal 

基本上在這個例子中b「隱藏」的值2,如果它是由a產生。 (我改編自this question的回答,這類似於包裝發電機,但方式稍有不同。但您可能會發現有用的討論)

但是,您在做這件事時需要非常小心。由於b可能會跳過a的值,因此b可能會跳過意想不到的位置中的值,因此任何嘗試將send值更改爲b的呼叫者都可能無法獲得預期的結果。在使用send的情況下,這通常意味着調用者監視發生器以獲取某些傳遞某些信息的屈服值,然後將值發送回來作爲響應。如果中間有b,則此通信可能會失去同步。例如,假設a產生一個「消息」,其中b產生給呼叫者,並且呼叫者發回響應,然後b然後發送到a。但假設b吞下a的迴應。這會給調用者帶來問題。您仍然可以使其工作,但這意味着您必須非常小心地編寫b以主要通過a的API預期的任何通信渠道。

+0

但是,我怎樣才能將發送到'b'的值傳回給'a'? – LateCoder

+0

@LateCoder:事情變得更加手動;你必須手動調用'send'並捕獲'StopIteration'。另外,當'b'跳過一個值時,你需要決定發送給'a'的東西。 – user2357112

+0

@LateCoder:我更新了我的答案。但是如果'b'可以跳過值,則需要注意使用'send',因爲這可能意味着調用者發送給'b'的值不會在「正確的時間」傳遞給'a',或者值從'a'不在'正確的時間'到達'b'的來電者。 – BrenBarn

0

由於看起來我無法將條件合併到yield from上,所以我通過處理我關心的手動解決了這個問題。這是我結束了:

def a(): 
    data = get_data_somewhere() 
    for val in data: 
     yield val 

def b(): 
    a_gen = a() 
    val = next(a_gen) 
    try: 
     while True: 
      if val == "foo": 
       received = yield val 
       val = a_gen.send(received) 
      else: 
       val = a_gen.send(None) 
    except StopIteration: 
     pass 
    finally: 
     del a_gen