IT story

루프의 Python Lambda

hot-time 2021. 1. 7. 20:02
반응형

루프의 Python Lambda


다음 코드 스 니펫을 고려하십시오.

# directorys == {'login': <object at ...>, 'home': <object at ...>}
for d in directorys:
    self.command["cd " + d] = (lambda : self.root.change_directory(d))

다음과 같이 두 가지 기능의 사전을 만들 것으로 예상합니다.

# Expected :
self.command == {
    "cd login": lambda: self.root.change_directory("login"),
    "cd home": lambda: self.root.change_directory("home")
}

그러나 생성 된 두 개의 람다 함수가 정확히 동일한 것처럼 보입니다.

# Result :
self.command == {
    "cd login": lambda: self.root.change_directory("login"),
    "cd home": lambda: self.root.change_directory("login")   # <- Why login ?
}

왜 그런지 정말 모르겠어요. 의견 있으십니까 ?


생성 된 각 함수에 대해 d를 바인딩해야합니다. 이를 수행하는 한 가지 방법은 기본값을 사용하여 매개 변수로 전달하는 것입니다.

lambda d=d: self.root.change_directory(d)

이제 함수 내부의 d는 이름이 같더라도 매개 변수를 사용하고 함수가 생성 될 때 그 기본값이 평가됩니다. 이것을 볼 수 있도록 :

lambda bound_d=d: self.root.change_directory(bound_d)

개체를 바인딩하기 때문에 목록 및 사전과 같은 변경 가능한 개체의 경우와 같이 기본값이 작동하는 방식을 기억하십시오.

기본값이있는이 매개 변수 관용구는 충분히 일반적이지만 함수 매개 변수를 조사하고 그 존재에 따라 수행 할 작업을 결정하면 실패 할 수 있습니다. 다른 클로저로 매개 변수를 피할 수 있습니다.

(lambda d=d: lambda: self.root.change_directory(d))()
# or
(lambda d: lambda: self.root.change_directory(d))(d)

이것은 d가 바인딩되는 지점 때문입니다. 람다 함수는 모두 현재 아닌 변수를 가리 키 므로 다음 반복에서 업데이트하면이 업데이트가 모든 함수에 표시됩니다.dd

더 간단한 예 :

funcs = []
for x in [1,2,3]:
  funcs.append(lambda: x)

for f in funcs:
  print f()

# output:
3
3
3

다음과 같이 추가 기능을 추가하여이 문제를 해결할 수 있습니다.

def makeFunc(x):
  return lambda: x

funcs = []
for x in [1,2,3]:
  funcs.append(makeFunc(x))

for f in funcs:
  print f()

# output:
1
2
3

람다 식 내부의 범위를 수정할 수도 있습니다.

lambda bound_x=x: bound_x

그러나 일반적으로 함수의 시그니처를 변경했기 때문에 이것은 좋은 습관 아닙니다 .


I met the same problem. The selected solution helped me a lot, but I consider necessary to add a precision to make functional the code of the question: define the lambda function outside of the loop. By the way, default value is not necessary.

foo = lambda d: lambda : self.root.change_directory(d)
for d in directorys:
    self.command["cd " + d] = (foo(d))

Alternatively, instead of lambda, you can use functools.partial which, in my opinion, has a cleaner syntax.

Instead of:

for d in directorys:
    self.command["cd " + d] = (lambda d=d: self.root.change_directory(d))

it will be:

for d in directorys:
    self.command["cd " + d] = partial(self.root.change_directory, d)

Or, here is another simple example:

numbers = [1, 2, 3]

lambdas = [lambda: print(number) 
           for number in numbers]
lambdas_with_binding = [lambda number=number: print(number) 
                        for number in numbers]
partials = [partial(print, number) 
            for number in numbers]

for function in lambdas:
    function()
# 3 3 3
for function in lambdas_with_binding:
    function()
# 1 2 3
for function in partials:
    function()
# 1 2 3

ReferenceURL : https://stackoverflow.com/questions/19837486/python-lambda-in-a-loop

반응형