복붙노트

[PYTHON] 리스트 독해력과 생성자 표현에서 산출

PYTHON

리스트 독해력과 생성자 표현에서 산출

다음과 같은 행동은 나에게 다소 직관적 인 것처럼 보인다 (Python 3.4) :

>>> [(yield i) for i in range(3)]
<generator object <listcomp> at 0x0245C148>
>>> list([(yield i) for i in range(3)])
[0, 1, 2]
>>> list((yield i) for i in range(3))
[0, None, 1, None, 2, None]

마지막 줄의 중간 값은 실제로 항상 None이 아닙니다. 다음과 같은 generator에 해당하는 것입니다.

def f():
   for i in range(3):
      yield (yield i)

그 세 줄이 전혀 작동하지 않는 것은 우스운 이야기입니다. Reference는 yield가 함수 정의에서만 허용된다고 말합니다. (내가 잘못 읽었을 수도 있고 단순히 구버전에서 복사했을 수도 있습니다). 처음 두 라인은 파이썬 2.7에서 SyntaxError를 생성하지만 세 번째 라인은 그렇지 않습니다.

또한 이상하게 보입니다.

누군가 더 많은 정보를 제공 할 수 있습니까?

해결법

  1. ==============================

    1.생성자 표현식과 집합 및 사전 생성물은 (생성기) 함수 개체로 컴파일됩니다. 파이썬 3에서,리스트 comprehensions은 같은 처리를 얻습니다; 본질적으로 새로운 중첩 범위입니다.

    생성자 표현식과 집합 및 사전 생성물은 (생성기) 함수 개체로 컴파일됩니다. 파이썬 3에서,리스트 comprehensions은 같은 처리를 얻습니다; 본질적으로 새로운 중첩 범위입니다.

    발전기 표현식을 해체하려고하면 다음과 같이 볼 수 있습니다.

    >>> dis.dis(compile("(i for i in range(3))", '', 'exec'))
      1           0 LOAD_CONST               0 (<code object <genexpr> at 0x10f7530c0, file "", line 1>)
                  3 LOAD_CONST               1 ('<genexpr>')
                  6 MAKE_FUNCTION            0
                  9 LOAD_NAME                0 (range)
                 12 LOAD_CONST               2 (3)
                 15 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
                 18 GET_ITER
                 19 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
                 22 POP_TOP
                 23 LOAD_CONST               3 (None)
                 26 RETURN_VALUE
    >>> dis.dis(compile("(i for i in range(3))", '', 'exec').co_consts[0])
      1           0 LOAD_FAST                0 (.0)
            >>    3 FOR_ITER                11 (to 17)
                  6 STORE_FAST               1 (i)
                  9 LOAD_FAST                1 (i)
                 12 YIELD_VALUE
                 13 POP_TOP
                 14 JUMP_ABSOLUTE            3
            >>   17 LOAD_CONST               0 (None)
                 20 RETURN_VALUE
    

    위의 그림은 생성자 표현식이 함수로로드 된 코드 객체로 컴파일 된 것을 보여줍니다 (MAKE_FUNCTION은 코드 객체에서 함수 객체를 만듭니다). .co_consts [0] 참조를 사용하면 표현식에 대해 생성 된 코드 객체를 볼 수 있으며 생성기 함수처럼 YIELD_VALUE를 사용합니다.

    이와 같이, yield 표현식은 컴파일러가 이들을 변장 함수로 간주하기 때문에 그러한 맥락에서 작동합니다.

    이것은 버그입니다. 수확량은 이러한 표현에서 아무런 자리가 없다. 파이썬 3.7 이전의 파이썬 문법은 이것을 허용합니다 (코드가 컴파일 가능한 이유입니다). 그러나 yield 표현식 스펙은 여기서 yield를 사용하면 실제로 작동하지 않아야 함을 보여줍니다.

    이것은 버그 10544의 버그로 확인되었습니다. 버그의 해결책은 yield와 yield를 사용하면 Python 3.8에서 SyntaxError가 발생한다는 것입니다. 파이썬 3.7에서는 DeprecationWarning을 발생시켜 코드가이 구조를 사용하여 멈추도록합니다. Python 3 호환성 경고를 가능하게하는 -3 명령 행 스위치를 사용하면 Python 2.7.15 이상에서 같은 경고를 볼 수 있습니다.

    3.7.0b1 경고는 다음과 같습니다. 경고를 오류로 바꾸면 3.8에서와 같이 SyntaxError 예외가 발생합니다.

    >>> [(yield i) for i in range(3)]
    <stdin>:1: DeprecationWarning: 'yield' inside list comprehension
    <generator object <listcomp> at 0x1092ec7c8>
    >>> import warnings
    >>> warnings.simplefilter('error')
    >>> [(yield i) for i in range(3)]
      File "<stdin>", line 1
    SyntaxError: 'yield' inside list comprehension
    

    리스트 이해력에서의 yield와 생성자 표현식에서의 yield 사이의 차이는이 두 표현식이 어떻게 구현되는지의 차이에서 비롯됩니다. 파이썬 3에서는리스트 이해력이 LIST_APPEND 호출을 사용하여 스택의 최상위를 생성되는리스트에 추가하는 반면, 생성자 표현식은 그 값을 산출합니다. in (yield )을 추가하면 다른 YIELD_VALUE 연산 코드가 다음 중 하나에 추가됩니다.

    >>> dis.dis(compile("[(yield i) for i in range(3)]", '', 'exec').co_consts[0])
      1           0 BUILD_LIST               0
                  3 LOAD_FAST                0 (.0)
            >>    6 FOR_ITER                13 (to 22)
                  9 STORE_FAST               1 (i)
                 12 LOAD_FAST                1 (i)
                 15 YIELD_VALUE
                 16 LIST_APPEND              2
                 19 JUMP_ABSOLUTE            6
            >>   22 RETURN_VALUE
    >>> dis.dis(compile("((yield i) for i in range(3))", '', 'exec').co_consts[0])
      1           0 LOAD_FAST                0 (.0)
            >>    3 FOR_ITER                12 (to 18)
                  6 STORE_FAST               1 (i)
                  9 LOAD_FAST                1 (i)
                 12 YIELD_VALUE
                 13 YIELD_VALUE
                 14 POP_TOP
                 15 JUMP_ABSOLUTE            3
            >>   18 LOAD_CONST               0 (None)
                 21 RETURN_VALUE
    

    바이트 코드 인덱스 15와 12에있는 YIELD_VALUE 연산 코드는 각각 여분의 것으로, 둥지에있는 뻐꾸기입니다. 따라서 list-comprehension-turned-generator의 경우 매번 스택의 윗부분을 생성하는 항복 (스택의 윗부분을 항복 (yield) 반환 값으로 바꾸는 것)과 생성자 표현의 변형 정수)를 반환하고 다시 yield하지만 스택에 yield의 반환 값이 포함되고 두 번째로 None을 얻습니다.

    목록 이해를 위해 의도 한 목록 객체 출력은 여전히 ​​반환되지만 Python 3에서는 이것을 생성기로 간주하므로 반환 값은 값 속성으로 StopIteration 예외에 대신 연결됩니다.

    >>> from itertools import islice
    >>> listgen = [(yield i) for i in range(3)]
    >>> list(islice(listgen, 3))  # avoid exhausting the generator
    [0, 1, 2]
    >>> try:
    ...     next(listgen)
    ... except StopIteration as si:
    ...     print(si.value)
    ... 
    [None, None, None]
    

    이러한 None 객체는 yield 표현식의 반환 값입니다.

    그리고 이것을 다시 강조하기 위해; 이 같은 문제는 Python 2와 Python 3에서도 사전과 집합의 이해에 적용됩니다. 파이썬 2에서 yield 반환 값은 여전히 ​​의도 된 사전이나 set 객체에 추가되고 반환 값은 StopIteration 예외에 첨부되지 않고 마지막으로 'yielded'됩니다.

    >>> list({(yield k): (yield v) for k, v in {'foo': 'bar', 'spam': 'eggs'}.items()})
    ['bar', 'foo', 'eggs', 'spam', {None: None}]
    >>> list({(yield i) for i in range(3)})
    [0, 1, 2, set([None])]
    
  2. from https://stackoverflow.com/questions/32139885/yield-in-list-comprehensions-and-generator-expressions by cc-by-sa and MIT license