map()과 목록 이해의 결과가 다른 이유는 무엇입니까?
-
02-07-2019 - |
문제
다음 테스트는 실패합니다.
#!/usr/bin/env python
def f(*args):
"""
>>> t = 1, -1
>>> f(*map(lambda i: lambda: i, t))
[1, -1]
>>> f(*(lambda: i for i in t)) # -> [-1, -1]
[1, -1]
>>> f(*[lambda: i for i in t]) # -> [-1, -1]
[1, -1]
"""
alist = [a() for a in args]
print(alist)
if __name__ == '__main__':
import doctest; doctest.testmod()
다시 말해서:
>>> t = 1, -1
>>> args = []
>>> for i in t:
... args.append(lambda: i)
...
>>> map(lambda a: a(), args)
[-1, -1]
>>> args = []
>>> for i in t:
... args.append((lambda i: lambda: i)(i))
...
>>> map(lambda a: a(), args)
[1, -1]
>>> args = []
>>> for i in t:
... args.append(lambda i=i: i)
...
>>> map(lambda a: a(), args)
[1, -1]
해결책
가치가 다르기 때문에 서로 다릅니다. i
생성기 표현식과 목록 comp 모두 느리게 평가됩니다. 즉,익명 함수가 호출될 때 f
.
그 무렵, i
다음과 같은 경우 마지막 값에 바인딩됩니다. t
, 이는 -1입니다.
따라서 기본적으로 이것은 목록 이해가 수행하는 작업입니다(genexp의 경우와 마찬가지로).
x = []
i = 1 # 1. from t
x.append(lambda: i)
i = -1 # 2. from t
x.append(lambda: i)
이제 람다는 참조하는 클로저를 가지고 다닙니다. i
, 하지만 i
두 경우 모두 -1에 바인딩됩니다. 왜냐하면 그것이 할당된 마지막 값이기 때문입니다.
람다가 현재 값을 수신하는지 확인하려면 i
, 하다
f(*[lambda u=i: u for i in t])
이런 식으로 평가를 강제로 수행합니다. i
클로저가 생성될 때.
편집하다:생성기 표현식과 목록 이해에는 한 가지 차이점이 있습니다.후자는 루프 변수를 주변 범위로 유출합니다.
다른 팁
Lambda는 값이 아닌 변수를 캡처하므로 코드가
lambda : i
항상 가치를 반환합니다 현재 폐쇄에 묶여 있습니다. 호출 될 때 까지이 값은 -1로 설정되었습니다.
원하는 것을 얻으려면 Lambda가 만들 때 실제 바인딩을 캡처해야합니다.
>>> f(*(lambda i=i: i for i in t)) # -> [-1, -1]
[1, -1]
>>> f(*[lambda i=i: i for i in t]) # -> [-1, -1]
[1, -1]
표현 f = lambda: i
다음과 같습니다.
def f():
return i
표현 g = lambda i=i: i
다음과 같습니다.
def g(i=i):
return i
i
a 무료 변수 첫 번째 경우에 두 번째 경우에 함수 매개 변수에 바인딩되며,이 경우 로컬 변수입니다. 기본 매개 변수의 값은 함수 정의 시점에서 평가됩니다.
발전기 표현식은 가장 가까운 동봉 범위입니다 (여기서 i
정의) i
이름의 이름 lambda
그러므로 표현 i
해당 블록에서 해결됩니다.
f(*(lambda: i for i in (1, -1)) # -> [-1, -1]
i
로컬 변수입니다 lambda i: ...
따라서 블록이므로 참조하는 객체는 해당 블록에 정의됩니다.
f(*map(lambda i: lambda: i, (1,-1))) # -> [1, -1]