将“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()
注意复杂的逻辑操作只需要将注册作为资源。