将“yield from”语句转换为Python 2.7代码

我在Python 3.2中有一个代码,我想在Python 2.7中运行它。 我没有转换它(已经把两个版本中的missing_elements的代码),但我不知道这是否是最有效的方式来做到这一点。 基本上,如果在missing_element函数中有两个yield from类似下面的调用的上半部分和下半部分,会发生什么情况? 来自两个半部分(上部和下部)的条目是否在一个列表中相互追加,以便父代recursion函数与yield from调用的yield from一起使用并将两个半部分一起使用?

 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 

如果你不使用你的收益率的结果,你总是可以把这个:

 yield from foo: 

…进入这个:

 for bar in foo: yield bar 

可能会有性能成本,但是从来没有语义上的差异。


来自两个半部分(上部和下部)的条目是否在一个列表中相互追加,以便父代recursion函数与来自调用的收益一起使用并将两个半部分一起使用?

没有! 迭代器和生成器的全部意义在于,不要构build实际列表并将它们附加在一起。

效果是相似的:你只从一个产出,然后从另一个产出。

如果你认为上半部分和下半部分是“懒惰列表”,那么你可以把它看作是一个“懒惰的附加”,它会创build一个更大的“懒惰列表”。 如果你调用父函数的结果list ,你当然得到一个实际的list ,相当于将两个列表相加在一起,如果你已经完成了yield list(…)而不是yield from …

但是我认为用相反的方式来思考它会更容易:它所做的和for循环完全相同。

如果你将两个迭代器保存到variables中,并且通过itertools.chain(upper, lower)循环,那么循环第一次,然后循环第二次,对吗? 这里没有区别。 事实上,你可以实现chain只是:

 for arg in *args: yield from arg 

*不是生成器向调用者发送的值,即生成器(来自调用者使用send方法)内的yieldexpression式本身的值,如PEP 342中所述 。 你没有在你的例子中使用这些。 而且我敢打赌,你不是真正的代码。 但是,协程风格的代码通常使用expression式中的yield from值,例如PEP 3156 。 这样的代码通常依赖于Python 3.3生成器的其他特性 – 特别是来自相同PEP 380的新的StopIteration.value ,它引入了yield from成果 – 所以它将不得不被重写。 但是,如果没有,你可以使用PEP也显示你完全可怕的杂乱等值,你当然可以减less你不关心的部分。 如果你不使用expression式的值,它会削减到上面的两行。

**不是一个巨大的,并且没有什么可以做的,因为它没有使用Python 3.3或完全重构您的代码。 这与将Python列表parsing转换为Python 1.5循环的情况完全相同,或者在XY版本中存在新的优化时需要使用旧版本的情况。

我刚刚遇到了这个问题,我的用法有点困难,因为我需要的收益 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 

希望这会帮助遇到同样问题的人。 🙂

我想我find了一种方法来模拟Python 2.x中的结构Python 3.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 

也许有人认为这有帮助。

已知问题:缺less对send()和PEP 380中描述的各种angular落案例的支持。这些可以添加,我将编辑我的项目,一旦我得到它的工作。

将它们replace为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 

那么使用pep-380的定义来构buildPython 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) 

除了像目前一样,exception不能被返回的生成器中的except子句捕获。

StopIterationexceptionperformance得如此定义:

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

我发现使用资源上下文(使用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() 

注意复杂的逻辑操作只需要将注册作为资源。