true - while en python 3
Python Lambda en un bucle (3)
Teniendo en cuenta el siguiente fragmento de código:
# directorys == {''login'': <object at ...>, ''home'': <object at ...>}
for d in directorys:
self.command["cd " + d] = (lambda : self.root.change_directory(d))
Espero crear un diccionario de dos funciones de la siguiente manera:
# Expected :
self.command == {
"cd login": lambda: self.root.change_directory("login"),
"cd home": lambda: self.root.change_directory("home")
}
pero parece que las dos funciones lambda generadas son exactamente las mismas:
# Result :
self.command == {
"cd login": lambda: self.root.change_directory("login"),
"cd home": lambda: self.root.change_directory("login") # <- Why login ?
}
Realmente no entiendo por qué. Tienes alguna sugerencia ?
Debe enlazar d para cada función creada. Una forma de hacerlo es pasarlo como un parámetro con un valor predeterminado:
lambda d=d: self.root.change_directory(d)
Ahora, la función d dentro de la función usa el parámetro, aunque tenga el mismo nombre, y el valor predeterminado para eso se evalúa cuando se crea la función. Para ayudarte a ver esto:
lambda bound_d=d: self.root.change_directory(bound_d)
Recuerde cómo funcionan los valores predeterminados, como para objetos mutables como listas y dictados, porque está vinculando un objeto.
Esta expresión de parámetros con valores predeterminados es lo suficientemente común, pero puede fallar si introspecta los parámetros de la función y determina qué hacer en función de su presencia. Puede evitar el parámetro con otro cierre:
(lambda d=d: lambda: self.root.change_directory(d))()
# or
(lambda d: lambda: self.root.change_directory(d))(d)
Mejor aún, un rediseño de la forma en que manejas los "comandos" ayudaría aquí y debería ayudar en otro lugar.
Esto se debe al punto en el que d se está vinculando. Las funciones lambda apuntan a la variable d
lugar del valor actual de la misma, por lo que cuando actualiza d
en la siguiente iteración, esta actualización se ve en todas sus funciones.
Para un ejemplo más simple:
funcs = []
for x in [1,2,3]:
funcs.append(lambda: x)
for f in funcs:
print f()
# output:
3
3
3
Puede evitar esto agregando una función adicional, como esta:
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
También puede corregir el alcance dentro de la expresión lambda
lambda bound_x=x: bound_x
Sin embargo, en general, esta no es una buena práctica ya que ha cambiado la firma de su función.
Me encontré con el mismo problema. La solución seleccionada me ayudó mucho, pero considero necesario agregar una precisión para hacer funcional el código de la pregunta: definir la función lambda fuera del ciclo. Por cierto, el valor predeterminado no es necesario.
foo = lambda d: lambda : self.root.change_directory(d)
for d in directorys:
self.command["cd " + d] = (foo(d))