설명
여기서 문제 i
는 함수 f
가 생성 될 때의 값이 저장되지 않는다는 것입니다. 오히려, f
의 값 조회 i
가 될 때 호출을 .
생각해 보면이 동작이 완벽하게 이해됩니다. 사실 함수가 작동 할 수있는 유일한 합리적인 방법입니다. 다음과 같이 전역 변수에 액세스하는 함수가 있다고 가정 해보십시오.
global_var = 'foo'
def my_function():
print(global_var)
global_var = 'bar'
my_function()
이 코드를 읽으면 global_var
함수가 선언 된 후의 값 이 변경 되었기 때문에 "foo"가 아닌 "bar"가 인쇄 될 것으로 예상 할 수 있습니다 . 자체 코드에서도 똑같은 일이 발생합니다.를 호출 할 때 f
의 값 i
이 변경되고로 설정되었습니다 2
.
해결책
실제로이 문제를 해결하는 방법에는 여러 가지가 있습니다. 다음은 몇 가지 옵션입니다.
i
기본 인수로 사용하여 초기 바인딩 강제
클로저 변수 (예 :)와 달리 i
기본 인수는 함수가 정의 될 때 즉시 평가됩니다.
for i in range(3):
def f(i=i): # <- right here is the important bit
return i
functions.append(f)
이것이 작동하는 방법 / 이유에 대한 약간의 통찰력을 제공하기 위해 : 함수의 기본 인수는 함수의 속성으로 저장됩니다. 따라서의 현재 값 i
이 스냅 샷되고 저장됩니다.
>>> i = 0
>>> def f(i=i):
... pass
>>> f.__defaults__ # this is where the current value of i is stored
(0,)
>>> # assigning a new value to i has no effect on the function's default arguments
>>> i = 5
>>> f.__defaults__
(0,)
함수 팩토리를 사용하여 i
클로저 의 현재 값을 캡처합니다.
문제의 근원은 i
변할 수있는 변수라는 것입니다. 절대 변경되지 않는 다른 변수를 만들어이 문제를 해결할 수 있습니다 . 가장 쉬운 방법은 클로저입니다 .
def f_factory(i):
def f():
return i # i is now a *local* variable of f_factory and can't ever change
return f
for i in range(3):
f = f_factory(i)
functions.append(f)
functools.partial
의 현재 값을에 바인딩하는 데 사용 i
합니다.f
functools.partial
기존 함수에 인수를 연결할 수 있습니다. 어떤면에서 그것도 일종의 함수 팩토리입니다.
import functools
def f(i):
return i
for i in range(3):
f_with_i = functools.partial(f, i) # important: use a different variable than "f"
functions.append(f_with_i)
주의 사항 : 이러한 솔루션 은 변수에 새 값을 할당 하는 경우에만 작동 합니다. 변수에 저장된 객체 를 수정 하면 동일한 문제가 다시 발생합니다.
>>> i = [] # instead of an int, i is now a *mutable* object
>>> def f(i=i):
... print('i =', i)
...
>>> i.append(5) # instead of *assigning* a new value to i, we're *mutating* it
>>> f()
i = [5]
i
기본 인수로 변경했지만 여전히 어떻게 변경 되었는지 확인하십시오 ! 코드가으로 변경 i
되면 다음 과 같이 의 복사본 을 i
함수에 바인딩해야합니다 .
def f(i=i.copy()):
f = f_factory(i.copy())
f_with_i = functools.partial(f, i.copy())