program story

리스트 이해는 루프 변수에 쓰지만 생성기는 쓰지 않는 이유는 무엇입니까?

inputbox 2020. 10. 17. 10:29
반응형

리스트 이해는 루프 변수에 쓰지만 생성기는 쓰지 않는 이유는 무엇입니까?


목록 이해력으로 무언가를 수행하면 지역 변수에 씁니다.

i = 0
test = any([i == 2 for i in xrange(10)])
print i

"9"가 인쇄됩니다. 그러나 생성기를 사용하면 지역 변수에 쓰지 않습니다.

i = 0
test = any(i == 2 for i in xrange(10))
print i

"0"이 인쇄됩니다.

이 차이에 대한 합당한 이유가 있습니까? 이것은 디자인 결정입니까, 아니면 생성자와 목록 이해가 구현되는 방식의 무작위 부산물입니까? 개인적으로 목록 이해력이 지역 변수에 쓰지 않으면 나에게 더 나아 보일 것입니다.


Python의 작성자 인 Guido van Rossum 은 Python 3에 균일하게 빌드 된 생성기 표현식 에 대해 작성할 때이를 언급합니다 .

또한 목록 이해와 생성기 표현식 간의 동등성을 개선하기 위해 Python 3에서 또 다른 변경을 수행했습니다. Python 2에서 목록 이해는 루프 제어 변수를 주변 범위로 "누수"합니다.

x = 'before'
a = [x for x in 1, 2, 3]
print x # this prints '3', not 'before'

이것은 목록 이해의 원래 구현의 결과물이었습니다. 수년 동안 파이썬의 "더러운 작은 비밀"중 하나였습니다. 목록 이해력을 눈부시게 빠르게 만들려는 의도적 인 타협으로 시작했으며 초보자에게는 일반적인 함정은 아니지만 때때로 사람들을 찌르는 경우가 있습니다. 생성기 표현식의 경우이를 수행 할 수 없습니다. 생성기 표현식은 생성기를 사용하여 구현되며 실행에는 별도의 실행 프레임이 필요합니다. 따라서 생성기 표현식 (특히 짧은 시퀀스를 반복하는 경우)은 목록 이해보다 효율성이 떨어졌습니다.

그러나 Python 3에서는 생성기 표현식과 동일한 구현 전략을 사용하여 목록 이해의 "더러운 작은 비밀"을 수정하기로 결정했습니다. 따라서 Python 3에서 위의 예제 (print (x)를 사용하도록 수정 한 후 :-)는 'before'를 인쇄하여 목록 이해의 'x'가 일시적으로 음영 처리되지만 주변의 'x'를 재정의하지 않음을 증명합니다. 범위.

따라서 Python 3에서는 더 이상 이런 일이 발생하지 않습니다.

흥미롭게도, Python 2의 dict comprehensions 도이 작업을 수행하지 않습니다. 이것은 대부분 dict 이해가 Python 3에서 백 포트되었고 이미 수정이 있었기 때문입니다.

이 주제를 다루는 다른 질문도 있지만 주제를 검색했을 때 이미 그 질문을 보았을 것입니다. ;)


으로 PEP 289 (발전기 식)는 설명한다 :

루프 변수 (단순 변수 또는 단순 변수의 튜플 인 경우)는 주변 함수에 노출되지 않습니다. 이는 구현을 용이하게하고 일반적인 사용 사례를보다 안정적으로 만듭니다.

구현상의 이유로 수행 된 것으로 보입니다.

개인적으로 목록 이해력이 지역 변수에 쓰지 않으면 나에게 더 나아 보일 것입니다.

PEP 289는이를 다음과 같이 명확히합니다.

목록 이해도 루프 변수를 주변 범위로 "누출"합니다. 이것은 Python 3.0에서도 변경되므로 Python 3.0의 목록 이해의 의미 론적 정의는 list ()와 동일합니다.

즉, 설명하는 동작은 Python 2에서 실제로 다르지만 Python 3에서는 수정되었습니다.


개인적으로 목록 이해력이 지역 변수에 쓰지 않으면 나에게 더 나아 보일 것입니다.

당신이 올바른지. 이것은 Python 3.x에서 수정되었습니다. 2.x에서는 동작이 변경되지 않았으므로이 구멍을 사용하는 기존 코드에 영향을주지 않습니다.


왜냐하면 ... 왜냐하면.

아니, 정말 그게 다야. 구현의 변덕. 그리고 틀림없이 파이썬 3에서 수정 되었기 때문에 버그 일 것입니다.


목록 이해가 실제로 구현되는 방식을 방황하는 부산물로 귀하의 질문에 대한 좋은 답변을 찾았습니다.

Python 2에서 간단한 목록 이해를 위해 생성 된 바이트 코드를 살펴보십시오.

>>> s = compile('[i for i in [1, 2, 3]]', '', 'exec')
>>> dis(s)
  1           0 BUILD_LIST               0
              3 LOAD_CONST               0 (1)
              6 LOAD_CONST               1 (2)
              9 LOAD_CONST               2 (3)
             12 BUILD_LIST               3
             15 GET_ITER            
        >>   16 FOR_ITER                12 (to 31)
             19 STORE_NAME               0 (i)
             22 LOAD_NAME                0 (i)
             25 LIST_APPEND              2
             28 JUMP_ABSOLUTE           16
        >>   31 POP_TOP             
             32 LOAD_CONST               3 (None)
             35 RETURN_VALUE  

그것은 본질적으로 간단한으로 번역됩니다 for-loop. 결과적으로 for-loops적용 과 동일한 의미가 적용됩니다.

a = []
for i in [1, 2, 3]
    a.append(i)
print(i) # 3 leaky

In the list-comprehension case, (C)Python uses a "hidden list name" and a special instruction LIST_APPEND to handle creation but really does nothing more than that.

So your question should generalize to why Python writes to the for loop variable in for-loops; that is nicely answered by a blog post from Eli Bendersky.

Python 3, as mentioned and by others, has changed the list-comprehension semantics to better match that of generators (by creating a separate code-object for the comprehension) and is essentially syntactic sugar for the following:

a = [i for i in [1, 2, 3]]

# equivalent to
def __f(it):
    _ = []
    for i in it
        _.append(i)
    return _
a = __f([1, 2, 3])

this won't leak because it doesn't run in the uppermost scope as the Python 2 equivalent does. The i is leaked, only in __f and then destroyed as a local variable to that function.

If you'd want, take a look at the byte-code generated for Python 3 by running dis('a = [i for i in [1, 2, 3]]'). You'll see how a "hidden" code-object is loaded and then a function call is made in the end.


One of the subtle consequences of the dirty secret described by poke above, is that list(...) and [...] does not have the same side-effects in Python 2:

In [1]: a = 'Before'
In [2]: list(a for a in range(5))
In [3]: a
Out[3]: 'Before'

So no side-effect for generator expression inside list-constructor, but the side-effect is there in a direct list-comprehension:

In [4]: [a for a in range(5)]
In [5]: a
Out[5]: 4

참고URL : https://stackoverflow.com/questions/19848082/why-do-list-comprehensions-write-to-the-loop-variable-but-generators-dont

반응형