2013-07-10 41 views
51

我在Python 3.2中有一個代碼,我想在Python 2.7中運行它。我確實轉換了它(已將missing_elements的代碼放在兩個版本中),但我不確定這是否是最有效的方法。基本上如果在missing_element函數中有兩個yield from調用如上半部分和下半部分會發生什麼情況?來自兩個半部分(上部和下部)的條目是否在一個列表中相互追加,以便與yield from調用的父遞歸函數一起使用這兩個半部分?將「yield from」語句轉換爲Python 2.7代碼

def missing_elements(L, start, end): # Python 3.2 
    if end - start <= 1: 
     if L[end] - L[start] > 1: 
      yield from range(L[start] + 1, L[end]) 
     return 

index = start + (end - start) // 2 

# is the lower half consecutive? 
consecutive_low = L[index] == L[start] + (index - start) 
if not consecutive_low: 
    yield from missing_elements(L, start, index) 

# is the upper part consecutive? 
consecutive_high = L[index] == L[end] - (end - index) 
if not consecutive_high: 
    yield from missing_elements(L, index, end) 

def main(): 
    L = [10, 11, 13, 14, 15, 16, 17, 18, 20] 
    print(list(missing_elements(L, 0, len(L)-1))) 
    L = range(10, 21) 
    print(list(missing_elements(L, 0, len(L)-1))) 

def missing_elements(L, start, end): # Python 2.7 
    return_list = []     
    if end - start <= 1: 
     if L[end] - L[start] > 1: 
      return range(L[start] + 1, L[end]) 

    index = start + (end - start) // 2 

    # is the lower half consecutive? 
    consecutive_low = L[index] == L[start] + (index - start) 
    if not consecutive_low: 
     return_list.append(missing_elements(L, start, index)) 

    # is the upper part consecutive? 
    consecutive_high = L[index] == L[end] - (end - index) 
    if not consecutive_high: 
     return_list.append(missing_elements(L, index, end)) 
    return return_list 

回答

63

如果你不使用你的收益率的結果,*您可以隨時此:

yield from foo: 

...這個:

for bar in foo: 
    yield bar 

有可能是一個性能成本**,但從來沒有語義上的差異。


Are the entries from the two halves (upper and lower) appended to each other in one list so that the parent recursion function with the yield from call and use both the halves together?

沒有!迭代器和生成器的全部意義在於,您不需要構建實際列表並將它們附加在一起。

效果是類似的:你只從一個產量,然後從另一個產量。

如果你認爲上半部分和下半部分是「懶惰列表」,那麼可以,你可以把它看作是一個「懶惰的附加」,它會創建一個更大的「懶惰列表」。如果你對父函數的結果調用list,你當然會得到一個實際的list,這相當於把你得到的兩個列表加在一起,如果你完成了yield list(…)而不是yield from …

但我認爲反過來想想它更容易:它的作用與for循環完全相同。

如果保存兩個迭代器變量,環繞在itertools.chain(upper, lower),這將是相同的循環在第一,然後遍歷第二,對不對?這裏沒有區別。事實上,你可以實現chain縮寫爲:

for arg in *args: 
    yield from arg 

*不是值發生器所產生它的調用者,產量表現自己的價值,在發電機內(使用它們來自呼叫者send方法),如PEP 342中所述。你的例子中沒有使用這些。而且我敢打賭,你並不是真正的代碼。但是,協程式代碼通常使用yield from表達式的值 - 請參見PEP 3156。這樣的代碼通常取決於Python 3.3生成器的其他特性 - 特別是來自PEP 380的新StopIteration.value,它引入了yield from,因此它必須被重寫。但是,如果沒有,你可以使用PEP也向你展示完全可怕的雜亂等價物,你當然可以減少你不關心的部分。如果你不使用表達式的值,它會削減到上面的兩行。

**不是一個巨大的,並且沒有什麼可以做的,因爲它沒有使用Python 3。3或完全重構您的代碼。它與將Python列表解析轉換爲Python 1.5循環的情況完全相同,或者在版本X.Y中存在新優化並且您需要使用舊版本時的任何其他情況。

+2

你可能想獲得'bar',不'foo'。 – ovgolovin

+0

@ovgolovin:謝謝;固定。 – abarnert

+0

遞歸調用如何工作?父母功能的「收益」是否結合了孩子中的兩個「收益」陳述?如果不是連續的低: 收益missing_elements(L,開始,索引) #是上部連續? consecutive_high = L [index] == L [end] - (end - index) if not consecutive_high: 從missing_elements(L,index,end)得到 – vkaul11

2

與for循環替換它們:

yield from range(L[start] + 1, L[end]) 

==> 

for i in range(L[start] + 1, L[end]): 
    yield i 

同有關的元素:

yield from missing_elements(L, index, end) 

==> 

for el in missing_elements(L, index, end): 
    yield el 
3

我想我找到了一種方法來模擬Python 3.x都有在Python 2.x版本yield from結構這不是有效的,它是一個小哈克,但在這裏它是:

import types 

def inline_generators(fn): 
    def inline(value): 
     if isinstance(value, InlineGenerator): 
      for x in value.wrapped: 
       for y in inline(x): 
        yield y 
     else: 
      yield value 
    def wrapped(*args, **kwargs): 
     result = fn(*args, **kwargs) 
     if isinstance(result, types.GeneratorType): 
      result = inline(_from(result)) 
     return result 
    return wrapped 

class InlineGenerator(object): 
    def __init__(self, wrapped): 
     self.wrapped = wrapped 

def _from(value): 
    assert isinstance(value, types.GeneratorType) 
    return InlineGenerator(value) 

用法:

@inline_generators 
def outer(x): 
    def inner_inner(x): 
     for x in range(1, x + 1): 
      yield x 
    def inner(x): 
     for x in range(1, x + 1): 
      yield _from(inner_inner(x)) 
    for x in range(1, x + 1): 
     yield _from(inner(x)) 

for x in outer(3): 
    print x, 

生成輸出:

1 1 1 2 1 1 2 1 2 3 

也許有人認爲這是很有幫助的。

已知問題:缺少對send()和PEP 380中描述的各種角落案例的支持。可以添加這些內容,一旦我開始工作,我將編輯我的條目。

+4

這個解決方案的優點在於比較早期的簡單解決方案,其中轉換爲for循環? – ToolmakerSteve

+0

這需要是一個ActiveState配方。 – refi64

+0

不錯的實施。只需重新發現Trollius項目(asyncio for Python <3.3)與'From'方法相同。它的實施當然是生產準備。 – jsbueno

0

我發現使用資源上下文(使用python-resources模塊)是一個在Python 2.7中實現子生成器的優雅機制。無論如何,我已經在使用資源上下文了。

如果在Python 3.3,你將有:

@resources.register_func 
def get_a_thing(type_of_thing): 
    if type_of_thing is "A": 
     yield from complicated_logic_for_handling_a() 
    else: 
     yield from complicated_logic_for_handling_b() 

def complicated_logic_for_handling_a(): 
    a = expensive_setup_for_a() 
    yield a 
    expensive_tear_down_for_a() 

def complicated_logic_for_handling_b(): 
    b = expensive_setup_for_b() 
    yield b 
    expensive_tear_down_for_b() 

在Python 2.7,你將有:

@resources.register_func 
def get_a_thing(type_of_thing): 
    if type_of_thing is "A": 
     with resources.complicated_logic_for_handling_a_ctx() as a: 
      yield a 
    else: 
     with resources.complicated_logic_for_handling_b_ctx() as b: 
      yield b 

@resources.register_func 
def complicated_logic_for_handling_a(): 
    a = expensive_setup_for_a() 
    yield a 
    expensive_tear_down_for_a() 

@resources.register_func 
def complicated_logic_for_handling_b(): 
    b = expensive_setup_for_b() 
    yield b 
    expensive_tear_down_for_b() 

注意如何複雜的邏輯運算只需要註冊爲資源。

4

我只是碰到這個問題,我使用的是一個有點困難,因爲我需要的返回值的yield from

result = yield from other_gen() 

這不能表示爲一個簡單的for循環,但可以被複制這個:

_iter = iter(other_gen()) 
try: 
    while True: #broken by StopIteration 
     yield next(_iter) 
except StopIteration as e: 
    if e.args: 
     result = e.args[0] 
    else: 
     result = None 

希望這會幫助遇到同樣問題的人。:)

1

怎麼樣才能利用PEP-380的定義來構建一個Python語法2版:

聲明:

RESULT = yield from EXPR 

在語義上等同於:

_i = iter(EXPR) 
try: 
    _y = next(_i) 
except StopIteration as _e: 
    _r = _e.value 
else: 
    while 1: 
     try: 
      _s = yield _y 
     except GeneratorExit as _e: 
      try: 
       _m = _i.close 
      except AttributeError: 
       pass 
      else: 
       _m() 
      raise _e 
     except BaseException as _e: 
      _x = sys.exc_info() 
      try: 
       _m = _i.throw 
      except AttributeError: 
       raise _e 
      else: 
       try: 
        _y = _m(*_x) 
       except StopIteration as _e: 
        _r = _e.value 
        break 
     else: 
      try: 
       if _s is None: 
        _y = next(_i) 
       else: 
        _y = _i.send(_s) 
      except StopIteration as _e: 
       _r = _e.value 
       break 
RESULT = _r 

發電機中,聲明:

return value 

在語義上等同於

raise StopIteration(value) 

不同之處在於,按照目前,異常不能由除了返回發生器內子句捕獲。

的StopIteration異常的行爲就好像正是如此定義:

class StopIteration(Exception): 

    def __init__(self, *args): 
     if len(args) > 0: 
      self.value = args[0] 
     else: 
      self.value = None 
     Exception.__init__(self, *args)