목록 이해력 및 생성기 표현식에서 산출
다음 동작은 나에게 다소 반 직관적 인 것 같습니다 (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그들이 무엇이든 우리는, send다음과 같은 발전기 (내 생각) 발전기로, 해당 :
def f():
for i in range(3):
yield (yield i)
이 세 줄이 전혀 작동하지 않는다는 사실이 저를 놀라게합니다. 참조 것을 말한다 yield(내가 잘못 읽는 될 수 있지만 및 / 또는 단순히 이전 버전에서 복사되어 있음)만을 함수 정의에서 허용됩니다. 처음 두 줄 SyntaxError은 Python 2.7에서 a 를 생성 하지만 세 번째 줄은 그렇지 않습니다.
또한 이상하게 보입니다
- 목록 이해는 목록이 아닌 생성자를 반환합니다.
- 목록으로 변환 된 생성기 표현식과 해당 목록 이해에는 다른 값이 포함됩니다.
누군가가 더 많은 정보를 제공 할 수 있습니까?
참고 : 이것은 CPython의
yieldin comprehensions 및 생성기 표현식 처리의 버그였으며 Python 3.8에서 수정되었으며 Python 3.7에서는 사용 중단 경고가 있습니다. Python 버그 보고서 및 Python 3.7 및 Python 3.8 의 새로운 기능 항목을 참조하십시오 .
생성기 표현식, set 및 dict 이해는 (generator) 함수 객체로 컴파일됩니다. Python 3에서 목록 이해는 동일한 처리를받습니다. 본질적으로 모두 새로운 중첩 범위입니다.
생성기 표현식을 디스 어셈블하려고하면 다음을 볼 수 있습니다.
>>> 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컴파일러는이를 위장 함수로 인식하므로 해당 컨텍스트에서 표현식이 작동합니다.
이것은 버그입니다. yield이 표현에는 자리가 없습니다. Python 3.7 이전 의 Python 문법 에서는이를 허용하지만 (이것이 코드를 컴파일 할 수있는 이유입니다), yield표현식 사양 은 yield여기서 사용하는 것이 실제로 작동하지 않아야 함을 보여줍니다 .
yield 표현식은 생성기 함수를 정의 할 때만 사용되므로 함수 정의 본문에서만 사용할 수 있습니다.
이것은 문제 10544 의 버그로 확인되었습니다 . 버그의 해상도는 사용하는 것입니다 yield및 yield from것 를 제기 SyntaxError파이썬 3.8 ; Python 3.7 에서는DeprecationWarning 이 구문을 사용하여 코드가 중지되도록 a 를 발생시킵니다. Python 3 호환성 경고를 활성화 하는 -3명령 줄 스위치 를 사용하면 Python 2.7.15 이상에서 동일한 경고가 표시됩니다 .
3.7.0b1 경고는 다음과 같습니다. 경고를 오류 SyntaxError로 바꾸면 3.8 에서처럼 예외 가 발생합니다 .
>>> [(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
The differences between how yield in a list comprehension and yield in a generator expression operate stem from the differences in how these two expressions are implemented. In Python 3 a list comprehension uses LIST_APPEND calls to add the top of the stack to the list being built, while a generator expression instead yields that value. Adding in (yield <expr>) just adds another YIELD_VALUE opcode to either:
>>> 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
The YIELD_VALUE opcode at bytecode indexes 15 and 12 respectively is extra, a cuckoo in the nest. So for the list-comprehension-turned-generator you have 1 yield producing the top of the stack each time (replacing the top of the stack with the yield return value), and for the generator expression variant you yield the top of the stack (the integer) and then yield again, but now the stack contains the return value of the yield and you get None that second time.
For the list comprehension then, the intended list object output is still returned, but Python 3 sees this as a generator so the return value is instead attached to the StopIteration exception as the value attribute:
>>> 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]
Those None objects are the return values from the yield expressions.
And to reiterate this again; this same issue applies to dictionary and set comprehension in Python 2 and Python 3 as well; in Python 2 the yield return values are still added to the intended dictionary or set object, and the return value is 'yielded' last instead of attached to the StopIteration exception:
>>> 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])]
참고URL : https://stackoverflow.com/questions/32139885/yield-in-list-comprehensions-and-generator-expressions
'Program Club' 카테고리의 다른 글
| RecyclerView의 빠른 스크롤 (0) | 2020.11.06 |
|---|---|
| 왜 std :: cout을 작성해야하고 std :: << (0) | 2020.11.05 |
| R에서 수정시 복사 의미는 정확히 무엇이며 표준 소스는 어디에 있습니까? (0) | 2020.11.05 |
| VS 코드 : "생성 된 코드를 찾을 수 없기 때문에 중단 점이 무시되었습니다."오류 (0) | 2020.11.05 |
| std :: tuple sizeof, 놓친 최적화입니까? (0) | 2020.11.05 |