루프에서 함수 만들기
루프 내부에 함수를 만들려고합니다.
functions = []
for i in range(3):
def f():
return i
# alternatively: f = lambda: i
functions.append(f)
문제는 모든 기능이 동일하게된다는 것입니다. 0, 1, 2를 반환하는 대신 세 함수 모두 2를 반환합니다.
print([f() for f in functions])
# expected output: [0, 1, 2]
# actual output: [2, 2, 2]
왜 이런 일이 발생하고 각각 0, 1, 2를 출력하는 3 개의 다른 함수를 얻으려면 어떻게해야합니까?
지연 바인딩에 문제가 있습니다. 각 함수는 i
가능한 한 늦게 조회됩니다 (따라서 루프가 끝난 후 호출 i
되면로 설정됩니다 2
).
초기 바인딩을 강제하여 쉽게 고칠 수 있습니다. 다음 def f():
과 def f(i=i):
같이 변경 하십시오.
def f(i=i):
return i
기본값 (오른쪽 i
에서 i=i
인수 이름에 대한 기본값입니다 i
왼쪽이다, i
에서가 i=i
)에서 조회됩니다 def
하지에 시간 call
시간, 그래서 본질적으로 그들은 특히 초기 바인딩을 찾고있는 방법이야.
f
추가 인수 를 받는 것이 걱정된다면 (따라서 잠재적으로 잘못 호출 될 수 있음) 클로저를 "함수 팩토리"로 사용하는 더 정교한 방법이 있습니다.
def make_f(i):
def f():
return i
return f
루프 f = make_f(i)
에서 def
문 대신 사용하십시오 .
설명
여기서 문제 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)
To give a little bit of insight into how/why this works: A function's default arguments are stored as an attribute of the function; thus the current value of
i
is snapshotted and saved.>>> 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,)
Use a function factory to capture the current value of
i
in a closureThe root of your problem is that
i
is a variable that can change. We can work around this problem by creating another variable that is guaranteed to never change - and the easiest way to do this is a closure: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)
Use
functools.partial
to bind the current value ofi
tof
functools.partial
lets you attach arguments to an existing function. In a way, it too is a kind of function factory.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)
Caveat: These solutions only work if you assign a new value to the variable. If you modify the object stored in the variable, you'll experience the same problem again:
>>> 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]
Notice how i
still changed even though we turned it into a default argument! If your code mutates i
, then you must bind a copy of i
to your function, like so:
def f(i=i.copy()):
f = f_factory(i.copy())
f_with_i = functools.partial(f, i.copy())
참고URL : https://stackoverflow.com/questions/3431676/creating-functions-in-a-loop
'Programing' 카테고리의 다른 글
Microsoft.Practices.ServiceLocation의 출처는 어디입니까? (0) | 2020.10.25 |
---|---|
적절한 Realm 사용 패턴 / 모범 사례? (0) | 2020.10.25 |
Boost Library 프로그램 옵션을 사용하는 필수 및 선택적 인수 (0) | 2020.10.24 |
컴파일러 오류 : "초기화 요소가 컴파일 시간 상수가 아닙니다." (0) | 2020.10.24 |
웹 사이트의 한 섹션을 번역하지 않도록 Google 번역에 어떻게 지시 할 수 있습니까? (0) | 2020.10.24 |