람다 함수 폐쇄는 무엇을 캡처합니까?
최근에 Python을 가지고 놀기 시작했는데, 폐쇄가 작동하는 방식에 특이한 점을 발견했습니다.다음 코드를 고려합니다.
adders=[None, None, None, None]
for i in [0,1,2,3]:
adders[i]=lambda a: i+a
print adders[1](3)
단일 입력을 받고 숫자로 추가된 입력을 반환하는 단순한 함수 배열을 구축합니다.는 에 구성되어 있습니다.for
는 리테이터가 '''를 반복하는 곳''에 있습니다.i
runs 0
로로 합니다.3
.lambda
캡처하는 함수가 생성됩니다.i
함수 입력에 추가합니다.은 두 입니다.lambda
3
파라미터로 지정합니다.놀랍게도 결과는6
.
★★★★★★★★★★★★★★★★를 기대했다.4
내 추론은 Python에서는 모든 것이 객체이기 때문에 모든 변수가 포인터라는 것이었다.「」를 lambda
closures の for for の for for for for for의 폐업i
되어 있을 i
, 이 경우, 이 경우,i
새 정수 객체를 할당하면 이전에 생성된 폐쇄에는 영향을 주지 않습니다.★★★★★★★★★★★★★★★★★★★★★★adders
디버거 내의 어레이는 디버거를 나타냅니다. ★★★★★lambda
입니다.i
,3
「」가 됩니다.adders[1](3)
(Return)6
.
그래서 나는 다음 사항에 대해 궁금해한다.
- 폐쇄가 정확히 무엇을 포착하는가?
- 가장 할 수 요?
lambda
의i
의i
그을값 꿀꿀?
루프(또는 목록 이해, 생성기 식 등)를 사용하는 경우에 대한 보다 접근하기 쉬운 실제 버전의 질문에 대해서는 루프(또는 이해)에서 함수(또는 람다) 생성을 참조하십시오.이 질문은 Python에서 코드의 기본 동작을 이해하는 데 초점이 맞춰져 있습니다.
Tkinter에서 버튼을 만드는 문제를 해결하려고 여기에 온 경우, 보다 구체적인 조언을 위해 루프 패스 명령어 인수를 위한 버튼을 에서 작성해 보십시오.
obj에 포함된 정확한 내용을 참조하십시오.__closure__?Python이 폐쇄를 구현하는 방법에 대한 기술적인 세부사항을 참조하십시오.'얼리 바인딩과 레이트바인딩의 차이점'을 참조해 주세요.관련 용어에 대해 설명합니다.
기본값을 사용하여 인수를 사용하여 변수를 강제로 캡처할 수 있습니다.
>>> for i in [0,1,2,3]:
... adders[i]=lambda a,i=i: i+a # note the dummy parameter with a default value
...
>>> print( adders[1](3) )
4
파라미터(명칭)를 선언하는 것이 목적입니다.i
(값)을i
)
폐쇄가 정확히 무엇을 포착하는가?
Python에서 닫힘은 어휘 범위를 사용합니다. 즉, 닫힘 변수가 생성된 변수의 이름과 범위를 기억합니다.그러나 여전히 바인딩이 지연되고 있습니다. 폐쇄가 생성될 때가 아니라 폐쇄의 코드가 사용될 때 이름이 검색됩니다.예제의 모든 함수는 동일한 범위에서 생성되며 동일한 변수 이름을 사용하기 때문에 항상 동일한 변수를 참조합니다.
대신 적어도 두 가지 방법으로 조기 바인딩을 얻을 수 있습니다.
가장 간결하지만 엄밀하게 동등하지는 않은 방법은 Adrien Plison이 추천한 방법입니다.추가 인수를 사용하여 람다를 생성하고 추가 인수의 기본값을 보존할 개체로 설정합니다.
보다 상세하게, 보다 견고하게 작성되는 각 람다에 대해 새로운 스코프를 작성할 수 있습니다.
>>> adders = [0,1,2,3] >>> for i in [0,1,2,3]: ... adders[i] = (lambda b: lambda a: b + a)(i) ... >>> adders[1](3) 4 >>> adders[2](3) 5
여기서의 스코프는 인수를 바인드하는 새로운 함수(간단함의 경우 다른 람다)를 사용하여 작성되며, 인수로 바인드할 값을 전달합니다.그러나 실제 코드에서는 람다 대신 일반 함수를 사용하여 새 스코프를 생성할 수 있습니다.
def createAdder(x): return lambda y: y + x adders = [createAdder(i) for i in range(4)]
완성도를 높이기 위해 두 번째 질문에 대한 또 다른 답변:functools 모듈에서 partial을 사용할 수 있습니다.
Chris Lutz가 제안한 연산자에서 add를 가져오면 다음과 같은 예가 있습니다.
from functools import partial
from operator import add # add(a, b) -- Same as a + b.
adders = [0,1,2,3]
for i in [0,1,2,3]:
# store callable object with first argument given as (current) i
adders[i] = partial(add, i)
print adders[1](3)
다음 코드를 고려합니다.
x = "foo"
def print_x():
print x
x = "bar"
print_x() # Outputs "bar"
나는 대부분의 사람들이 이것을 전혀 헷갈리지 않을 것이라고 생각한다.그것은 예상된 행동이다.
그렇다면, 왜 사람들은 그것이 순환적으로 이루어졌을 때 다를 것이라고 생각할까요?나도 그 실수를 한 건 알지만 왜 그랬는지 모르겠어.루프라고?아니면 람다?
결국, 루프는 다음의 단축판일 뿐입니다.
adders= [0,1,2,3]
i = 0
adders[i] = lambda a: i+a
i = 1
adders[i] = lambda a: i+a
i = 2
adders[i] = lambda a: i+a
i = 3
adders[i] = lambda a: i+a
다음은 폐쇄의 데이터 구조와 내용을 강조하여 폐쇄 컨텍스트가 "저장"된 시기를 명확히 하는 새로운 예입니다.
def make_funcs():
i = 42
my_str = "hi"
f_one = lambda: i
i += 1
f_two = lambda: i+1
f_three = lambda: my_str
return f_one, f_two, f_three
f_1, f_2, f_3 = make_funcs()
폐쇄에는 무엇이 있습니까?
>>> print f_1.func_closure, f_1.func_closure[0].cell_contents
(<cell at 0x106a99a28: int object at 0x7fbb20c11170>,) 43
특히 my_str은 f1의 닫힘 상태가 아닙니다.
F2가 폐쇄된 건 뭐죠?
>>> print f_2.func_closure, f_2.func_closure[0].cell_contents
(<cell at 0x106a99a28: int object at 0x7fbb20c11170>,) 43
(메모리 주소로부터) 양쪽 클로저가 같은 오브젝트를 포함하고 있는 것에 주의해 주세요.따라서 람다 함수는 스코프를 참조하는 것으로 간주할 수 있습니다.단, my_str은 f_1 또는 f_2의 closure에 포함되지 않으며, i는 f_3의 closure에 포함되지 않습니다(표시되지 않음). 즉, closure 객체 자체가 별개의 객체임을 나타냅니다.
폐쇄 객체 자체가 동일한 객체입니까?
>>> print f_1.func_closure is f_2.func_closure
False
두 번째 질문에 대한 답변으로 가장 우아한 방법은 배열 대신 두 개의 매개 변수를 사용하는 함수를 사용하는 것입니다.
add = lambda a, b: a + b
add(1, 3)
그러나 여기서 람다를 사용하는 것은 조금 바보같다.Python은 우리에게operator
module: 기본 오퍼레이터에 대한 기능 인터페이스를 제공합니다.위의 람다에는 덧셈 연산자를 호출하는 데만 불필요한 오버헤드가 있습니다.
from operator import add
add(1, 3)
언어 탐색을 위해 장난을 치는 것은 이해하지만, Python의 스코핑의 이상함에 방해가 되는 일련의 함수를 사용할 수 있는 상황은 상상할 수 없습니다.
필요에 따라 어레이 인덱싱 구문을 사용하는 작은 클래스를 작성할 수 있습니다.
class Adders(object):
def __getitem__(self, item):
return lambda a: a + item
adders = Adders()
adders[1](3)
의 범위를 정리하는 한 가지 방법i
다른 스코프에서 람다를 생성하고(닫기 함수), 람다가 생성되는 데 필요한 파라미터를 전달합니다.
def get_funky(i):
return lambda a: i+a
adders=[None, None, None, None]
for i in [0,1,2,3]:
adders[i]=get_funky(i)
print(*(ar(5) for ar in adders))
부여5 6 7 8
물론이야.
언급URL : https://stackoverflow.com/questions/2295290/what-do-lambda-function-closures-capture
'IT' 카테고리의 다른 글
PHP cURL HTTP PUT (0) | 2022.10.30 |
---|---|
다른 테이블에 존재하지 않음으로써 SQL의 특정 행 표시 (0) | 2022.10.30 |
json_encode()를 사용하여 PHP 어레이에서 JSON 어레이로. (0) | 2022.10.30 |
JSONObject와 JSONArray의 차이점 (0) | 2022.10.30 |
추출하다Android에서 비활성 InputConnection 경고 텍스트 (0) | 2022.10.30 |