development

"yield from"문을 Python 2.7 코드로 변환

big-blog 2020. 11. 21. 09:37
반응형

"yield from"문을 Python 2.7 코드로 변환


Python 3.2에 아래 코드가 있었고 Python 2.7에서 실행하고 싶었습니다. 나는 그것을 변환했습니다 ( missing_elements두 버전 모두에 코드를 넣었습니다 ).하지만 그것이 가장 효율적인 방법인지 확실하지 않습니다. 기본적으로 함수의 yield from상반부와 하반부에 아래와 같은 두 개의 호출 이 있으면 어떻게 missing_element될까요? 부모 재귀가 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

성능 비용이있을 수 있지만 ** 의미 상 차이는 없습니다.


부모 재귀가 yield from call과 함께 두 반쪽을 함께 사용하도록 두 반쪽 (위쪽과 아래쪽)의 항목이 하나의 목록에 서로 추가됩니까?

아니! 반복기와 생성기의 요점은 실제 목록을 작성하고 함께 추가하지 않는다는 것입니다.

하지만 그 효과 는 비슷합니다. 하나에서 양보 한 다음 다른 사람에서 양보합니다.

위쪽 절반과 아래쪽 절반을 "지연 목록"으로 생각하면 더 큰 "지연 목록"을 생성하는 "지연 추가"로 생각할 수 있습니다. 당신이 호출하는 경우 그리고 list부모 함수의 결과에, 물론 당신은 실제 얻을 list함께 작업을 완료 더라면 당신이받은 것 두 목록 추가에 해당의 그 yield list(…)대신을 yield from ….

그러나 나는 그것을 다른 방향으로 생각하는 것이 더 쉽다고 생각합니다 : 그것이하는 일은 for루프가 하는 것과 똑같습니다 .

두 반복자를 변수에 저장하고을 itertools.chain(upper, lower)반복하면 첫 번째 반복을 반복 한 다음 두 번째 반복을 반복하는 것과 같습니다. 여기에 차이가 없습니다. 실제로 다음 chain과 같이 구현할 수 있습니다.

for arg in *args:
    yield from arg

* PEP 342에send 설명 된대로 생성기가 호출자에게 산출하는 값이 아니라 생성기 내에서 yield 표현식 자체의 값 ( 메소드를 사용하여 호출자로부터 생성됨 ) . 당신은 당신의 예제에서 이것을 사용하지 않습니다. 그리고 나는 당신이 당신의 실제 코드에 있지 않다고 확신합니다. 그러나 코 루틴 스타일 코드는 종종 표현식 의 값을 사용합니다 . 예제는 PEP 3156참조하십시오 . 이러한 코드는 일반적으로 Python 3.3 생성기의 다른 기능, 특히 도입 된 동일한 PEP 380 의 새로운 기능에 의존합니다.yield fromStopIteration.valueyield from-따라서 다시 작성해야합니다. 그러나 그렇지 않다면 PEP를 사용하면 완전히 끔찍한 지저분한 등가물을 보여줄 수 있으며 물론 신경 쓰지 않는 부분을 줄일 수 있습니다. 식의 값을 사용하지 않으면 위의 두 줄로 축소됩니다.

** 크지 않고 Python 3.3을 사용하거나 코드를 완전히 재구성하는 것 외에는 할 수있는 일이 없습니다. 목록 이해를 Python 1.5 루프로 번역하는 것과 똑같은 경우이거나 버전 XY에 새로운 최적화가 있고 이전 버전을 사용해야하는 다른 경우입니다.


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

방금이 문제를 발견했으며 반환 값필요했기 때문에 사용이 조금 더 어려웠습니다 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

바라건대 이것은 같은 문제를 겪는 사람들에게 도움이 될 것입니다. :)


yield fromPython 2.x에서 Python 3.x 구조 를 에뮬레이트하는 방법을 찾은 것 같습니다 . 효율적이지 않고 약간 엉망이지만 여기에 있습니다.

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

누군가 이것이 도움이 될 수 있습니다.

알려진 문제 : PEP 380에 설명 된 send () 및 다양한 코너 케이스에 대한 지원이 부족합니다. 추가 될 수 있으며 작업이 완료되면 항목을 편집하겠습니다.


Python 2 구문 버전을 구성하기 위해 pep-380 의 정의를 사용하는 것은 어떻습니까?

진술 :

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)

except that, as currently, the exception cannot be caught by except clauses within the returning generator.

The StopIteration exception behaves as though defined thusly:

class StopIteration(Exception):

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

I've found using resource contexts (using the python-resources module) to be an elegant mechanism for implementing subgenerators in Python 2.7. Conveniently I'd already been using the resource contexts anyway.

If in Python 3.3 you would have:

@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()

In Python 2.7 you would have:

@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()

Note how the complicated-logic operations only require the registration as a resource.

참고URL : https://stackoverflow.com/questions/17581332/converting-yield-from-statement-to-python-2-7-code

반응형