복붙노트

[PYTHON] 이상한 행동 : 목록 이해력의 람다

PYTHON

이상한 행동 : 목록 이해력의 람다

파이썬 2.6에서 :

[x() for x in [lambda: m for m in [1,2,3]]]

결과 :

[3, 3, 3]

출력은 [1, 2, 3]이 될 것으로 기대합니다. 나는 비 목록 이해 (non list comprehension) 접근법으로도 똑같은 문제를 겪는다. 그리고 m을 다른 변수에 복사 한 후에도 마찬가지입니다.

내가 뭘 놓치고 있니?

해결법

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

    1.람다가 m의 값을 기억하게하려면, 인자에 디폴트 값을 사용할 수있다.

    람다가 m의 값을 기억하게하려면, 인자에 디폴트 값을 사용할 수있다.

    [x() for x in [lambda m=m: m for m in [1,2,3]]]
    # [1, 2, 3]
    

    이것은 기본값이 정의 시간에 한 번 설정되기 때문에 작동합니다. 이제 각 람다는 람다 실행 시간에 외부 범위에서 m 값을 찾는 대신 자체 기본값 인 m을 사용합니다.

  2. ==============================

    2.발생하는 효과를 클로저라고하며, 로컬이 아닌 변수를 참조하는 함수를 정의 할 때이 함수는 자체 복사본을 가져 오는 대신 변수에 대한 참조를 유지합니다. 예를 들어, 코드를 람다 나 독해없이 동등한 버전으로 확장 할 것입니다.

    발생하는 효과를 클로저라고하며, 로컬이 아닌 변수를 참조하는 함수를 정의 할 때이 함수는 자체 복사본을 가져 오는 대신 변수에 대한 참조를 유지합니다. 예를 들어, 코드를 람다 나 독해없이 동등한 버전으로 확장 할 것입니다.

    inner_list = []
    for m in [1, 2, 3]:
        def Lambda():
             return m
        inner_list.append(Lambda)
    

    따라서이 시점에서 inner_list에는 세 개의 함수가 있고 각 함수는 호출 될 때 m의 값을 반환합니다. 그러나 두드러진 점은 그들이 모두 똑같은 m을 보았다는 것입니다. m이 변하고 있더라도 훨씬 나중에 호출 될 때까지 결코 그것을 보지 않습니다.

    outer_list = []
    for x in inner_list:
        outer_list.append(x())
    

    특히, 내부 목록은 외부 목록이 작성되기 전에 완전히 구성되기 때문에 m은 이미 마지막 값인 3에 도달했으며 세 함수 모두 동일한 값을 참조합니다.

  3. ==============================

    3.길게 이야기하면, 당신은 이것을 원하지 않습니다. 좀 더 구체적으로, 당신이 겪고있는 것은 운영 문제의 순서입니다. 당신은 모두 m을 반환하는 3 개의 분리 된 람다를 만들고 있습니다 만, 그 중 아무도 즉시 호출되지 않습니다. 그런 다음, 외부 목록 이해에 도달하면 모두 m이라는 잔여 값은 내부 목록 이해력의 마지막 값인 3입니다.

    길게 이야기하면, 당신은 이것을 원하지 않습니다. 좀 더 구체적으로, 당신이 겪고있는 것은 운영 문제의 순서입니다. 당신은 모두 m을 반환하는 3 개의 분리 된 람다를 만들고 있습니다 만, 그 중 아무도 즉시 호출되지 않습니다. 그런 다음, 외부 목록 이해에 도달하면 모두 m이라는 잔여 값은 내부 목록 이해력의 마지막 값인 3입니다.

    - 댓글 용 -

    >>> [lambda: m for m in range(3)]
    [<function <lambda> at 0x021EA230>, <function <lambda> at 0x021EA1F0>, <function <lambda> at 0x021EA270>]
    

    그것들은 3 개의 분리 된 람다들입니다.

    그리고 더 많은 증거로서 :

    >>> [id(m) for m in [lambda: m for m in range(3)]]
    [35563248, 35563184, 35563312]
    

    다시 세 가지 ID가 있습니다.

  4. ==============================

    4.함수의 __closure__를보십시오. 3은 모두 동일한 셀 객체를 가리키며, 이는 외부 범위에서 m에 대한 참조를 유지합니다.

    함수의 __closure__를보십시오. 3은 모두 동일한 셀 객체를 가리키며, 이는 외부 범위에서 m에 대한 참조를 유지합니다.

    >>> print(*[x.__closure__[0] for x in [lambda: m for m in [1,2,3]]], sep='\n')
    <cell at 0x00D17610: int object at 0x1E2139A8>
    <cell at 0x00D17610: int object at 0x1E2139A8>
    <cell at 0x00D17610: int object at 0x1E2139A8>
    

    unubtu의 답에 따라 함수가 키워드 인수로 m을 사용하지 않게하려면 각 반복에서 m을 평가하기 위해 추가 람다를 사용할 수 있습니다.

    >>> [x() for x in [(lambda x: lambda: x)(m) for m in [1,2,3]]]
    [1, 2, 3]
    
  5. ==============================

    5.개인적으로, 나는 이것을 더 우아한 해결책으로 생각한다. 람다 (Lambda)는 함수를 반환하므로 함수를 사용하고자한다면 그것을 사용해야합니다. 람다와 생성기에서 '익명'변수에 대해 동일한 기호를 사용하는 것은 혼란입니다. 따라서 제 예제에서는 다른 기호를 사용하여 더 명확하게 나타냅니다.

    개인적으로, 나는 이것을 더 우아한 해결책으로 생각한다. 람다 (Lambda)는 함수를 반환하므로 함수를 사용하고자한다면 그것을 사용해야합니다. 람다와 생성기에서 '익명'변수에 대해 동일한 기호를 사용하는 것은 혼란입니다. 따라서 제 예제에서는 다른 기호를 사용하여 더 명확하게 나타냅니다.

    >>> [ (lambda a:a)(i) for i in range(3)]
    [0, 1, 2]
    >>> 
    

    그것은 더 빠릅니다.

    >>> timeit.timeit('[(lambda a:a)(i) for i in range(10000)]',number=10000)
    9.231263160705566
    >>> timeit.timeit('[lambda a=i:a  for i in range(10000)]',number=10000)
    11.117988109588623
    >>> 
    

    지도만큼 빠르지는 않습니다.

    >>> timeit.timeit('map(lambda a:a,  range(10000))',number=10000)
    5.746963977813721
    

    (필자는이 테스트를 두 번 이상 실행했는데, 결과는 같았습니다. 파이썬 2.7에서 수행되었습니다. 결과는 파이썬 3에서 다릅니다. 두 목록의 이해도가 훨씬 더 비슷하고 훨씬 더 느리며 맵은 훨씬 빠릅니다.)

  6. ==============================

    6.나는 그것도 알아 차렸다. 나는 람다 (lambda)가 단 한 번만 만들어 졌다고 결론을 내렸다. 따라서 실제로 내부 목록 이해력은 m의 마지막 값과 관련된 3 개의 indentical 함수를 제공합니다.

    나는 그것도 알아 차렸다. 나는 람다 (lambda)가 단 한 번만 만들어 졌다고 결론을 내렸다. 따라서 실제로 내부 목록 이해력은 m의 마지막 값과 관련된 3 개의 indentical 함수를 제공합니다.

    그것을 시도하고 요소의 id ()를 확인하십시오.

    [참고 :이 답변은 정확하지 않습니다. 의견보기]

  7. from https://stackoverflow.com/questions/7368522/weird-behavior-lambda-inside-list-comprehension by cc-by-sa and MIT license