루프(또는 이해)에서 함수(또는 람다) 만들기
루프 내부에 함수를 만들려고 합니다.
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()
이 를 읽을 때, "foo가 아니라 "bar"로 될 것이라고 것입니다. "", "foo"의 은 "bar"의 값이기 때문입니다. 왜냐하면, 값이global_var
함수가 선언된 후 변경되었습니다.사용자 코드에서도 동일한 현상이 발생합니다.에 전화할 때까지.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())
@Aran-Fey의 훌륭한 답변에 추가하기 위해, 두 번째 솔루션에서는 키워드로 수행할 수 있는 함수 내부의 변수를 수정하기를 원할 수도 있습니다.nonlocal
:
def f_factory(i):
def f(offset):
nonlocal i
i += offset
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)
print(f(10))
은 각각의 저합니야다장해각각을 .i
메모리의 별도 공간에 값을 입력합니다. 예:
class StaticValue:
val = None
def __init__(self, value: int):
StaticValue.val = value
@staticmethod
def get_lambda():
return lambda x: x*StaticValue.val
class NotStaticValue:
def __init__(self, value: int):
self.val = value
def get_lambda(self):
return lambda x: x*self.val
if __name__ == '__main__':
def foo():
return [lambda x: x*i for i in range(4)]
def bar():
return [StaticValue(i).get_lambda() for i in range(4)]
def foo_repaired():
return [NotStaticValue(i).get_lambda() for i in range(4)]
print([x(2) for x in foo()])
print([x(2) for x in bar()])
print([x(2) for x in foo_repaired()])
Result:
[6, 6, 6, 6]
[6, 6, 6, 6]
[0, 2, 4, 6]
다음과 같이 시도할 수 있습니다.
l=[]
for t in range(10):
def up(y):
print(y)
l.append(up)
l[5]('printing in 5th function')
마지막 행을 수정합니다.
functions.append(f())
는 다음과 같은 이유 입니다.f
함수입니다. 파이썬은 함수를 일등 시민으로 취급하며 나중에 호출할 변수로 전달할 수 있습니다.그래서 당신의 원래 코드가 하는 일은 함수 자체를 목록에 추가하는 것이고, 당신이 하고 싶은 일은 함수의 결과를 목록에 추가하는 것인데, 이것은 위의 줄이 함수를 호출함으로써 달성하는 것입니다.
언급URL : https://stackoverflow.com/questions/3431676/creating-functions-or-lambdas-in-a-loop-or-comprehension
'programing' 카테고리의 다른 글
join을 사용하여 "not in ()" SQL 쿼리를 쓰는 방법 (0) | 2023.07.12 |
---|---|
Android가 텍스트 편집에 자리 표시자 텍스트 추가 (0) | 2023.07.12 |
봄 부팅 웹 앱이 준비된 후 자동으로 브라우저 시작 (0) | 2023.07.12 |
R2DBC - Oracle 데이터베이스 지원 (0) | 2023.07.12 |
데이터 그리드 보기에서 셀 강조 표시 사용 안 함 (0) | 2023.07.07 |