Sintaxis detrás de ordenado (clave = lambda: ...)

152

No entiendo muy bien la sintaxis detrás del sorted()argumento:

key=lambda variable: variable[0]

¿No es lambdaarbitrario? ¿Por qué se variableindica dos veces en lo que parece un dict?

Christopher Markieta
fuente

Respuestas:

162

keyes una función que se llamará para transformar los elementos de la colección antes de que se comparen. El parámetro pasado a keydebe ser algo invocable.

El uso de lambdacrea una función anónima (que es invocable). En el caso del sortedinvocable solo toma un parámetro. Python lambdaes bastante simple. Solo puede hacer y devolver una cosa realmente.

La sintaxis de lambdaes la palabra lambdaseguida de la lista de nombres de parámetros y luego un solo bloque de código. La lista de parámetros y el bloque de código están delineados por dos puntos. Esto es similar a otras construcciones en pitón, así como while, for, ify así sucesivamente. Todas son declaraciones que generalmente tienen un bloque de código. Lambda es solo otra instancia de una declaración con un bloque de código.

Podemos comparar el uso de lambda con el de def para crear una función.

adder_lambda = lambda parameter1,parameter2: parameter1+parameter2
def adder_regular(parameter1, parameter2): return parameter1+parameter2

lambda simplemente nos da una forma de hacerlo sin asignar un nombre. Lo que lo hace ideal para usar como parámetro de una función.

variable se usa dos veces aquí porque en el lado izquierdo del colon es el nombre de un parámetro y en el lado derecho se está usando en el bloque de código para calcular algo.

Evan
fuente
10
nota (a OP): debe evitarse asignar un lambda a un nombre (es decir, usarlo de una manera distinta a la función anónima). si te encuentras haciendo esto, probablemente deberías usar a def.
wim
151

Creo que todas las respuestas aquí cubren el núcleo de lo que hace la función lambda en el contexto de sorted () bastante bien, sin embargo, todavía siento que falta una descripción que conduzca a una comprensión intuitiva, así que aquí están mis dos centavos.

En aras de la exhaustividad, declararé lo obvio por adelantado: sorted () devuelve una lista de elementos ordenados y si queremos ordenar de una manera particular o si queremos ordenar una lista compleja de elementos (por ejemplo, listas anidadas o una lista de tuplas) podemos invocar el argumento clave.

Para mí, la comprensión intuitiva del argumento clave, por qué tiene que ser invocable y el uso de lambda como la función invocable (anónima) para lograr esto viene en dos partes.

  1. Usar lamba en última instancia significa que no tiene que escribir (definir) una función completa, como la que sblom proporcionó como ejemplo. Las funciones de Lambda se crean, usan e inmediatamente se destruyen, por lo que no arruinan su código con más código que solo se usará una vez. Esto, según tengo entendido, es la utilidad central de la función lambda y sus aplicaciones para tales roles son amplias. Su sintaxis es puramente por convención, que es esencialmente la naturaleza de la sintaxis programática en general. Aprenda la sintaxis y termine con ella.

La sintaxis de Lambda es la siguiente:

lambda input_variable (s) : sabroso one liner

p.ej

In [1]: f00 = lambda x: x/2

In [2]: f00(10)
Out[2]: 5.0

In [3]: (lambda x: x/2)(10)
Out[3]: 5.0

In [4]: (lambda x, y: x / y)(10, 2)
Out[4]: 5.0

In [5]: (lambda: 'amazing lambda')() # func with no args!
Out[5]: 'amazing lambda'
  1. La idea detrás del keyargumento es que debe tomar un conjunto de instrucciones que esencialmente apuntará la función 'sorted ()' en los elementos de la lista que deben usarse para ordenar. Cuando dice key=, lo que realmente significa es: a medida que recorro la lista un elemento a la vez (es decir, para e en la lista), voy a pasar el elemento actual a la función que proporciono en el argumento clave y usarlo para crear una lista transformada que me informará sobre el orden de la lista ordenada final.

Echale un vistazo:

mylist = [3,6,3,2,4,8,23]
sorted(mylist, key=WhatToSortBy)

Ejemplo base:

sorted(mylist)

[2, 3, 3, 4, 6, 8, 23] # todos los números están en orden de pequeño a grande.

Ejemplo 1:

mylist = [3,6,3,2,4,8,23]
sorted(mylist, key=lambda x: x%2==0)

[3, 3, 23, 6, 2, 4, 8] # ¿Te resulta intuitivo este resultado ordenado?

Tenga en cuenta que mi función lambda dijo ordenado para verificar si (e) era par o impar antes de ordenar.

¡PERO ESPERA! Puede (o quizás debería) preguntarse dos cosas: primero, ¿por qué mis probabilidades están antes de mis pares? (Ya que mi valor clave parece estar diciéndole a mi función ordenada que priorice los pares usando el operador mod x%2==0) Segundo, ¿por qué mis pares están fuera de servicio? 2 viene antes que 6 ¿verdad? Al analizar este resultado, aprenderemos algo más profundo sobre cómo funciona el argumento sorted ('key'), especialmente en conjunción con la función lambda anónima.

En primer lugar, notará que si bien las probabilidades se presentan antes de las igualaciones, las mismas no están ordenadas. ¿¿Por qué es esto?? Leamos los documentos :

Funciones clave A partir de Python 2.4, list.sort () y sorted () agregaron un parámetro clave para especificar una función que se invocará en cada elemento de la lista antes de realizar comparaciones.

Aquí tenemos que leer un poco entre líneas, pero lo que esto nos dice es que la función de clasificación solo se llama una vez, y si especificamos el argumento clave, clasificamos por el valor al que nos señala la función clave.

Entonces, ¿qué devuelve el ejemplo con un módulo? Un valor booleano: True == 1, False == 0. Entonces, ¿cómo se soluciona ordenado con esta clave? Básicamente transforma la lista original en una secuencia de 1s y 0s.

[3,6,3,2,4,8,23] se convierte en [0,1,0,1,1,1,0]

Ahora estamos llegando a alguna parte. ¿Qué obtienes cuando ordenas la lista transformada?

[0,0,0,1,1,1,1]

Bien, ahora sabemos por qué las probabilidades se presentan antes de los pares. Pero la siguiente pregunta es: ¿por qué el 6 todavía aparece antes que el 2 en mi lista final? Bueno, eso es fácil, ¡es porque la clasificación solo ocurre una vez! es decir, esos 1 todavía representan los valores de la lista original, que están en sus posiciones originales entre sí. Dado que la ordenación solo ocurre una vez, y no llamamos a ningún tipo de función de ordenación para ordenar los valores pares originales de menor a mayor, esos valores permanecen en su orden original entre sí.

La pregunta final es esta: ¿Cómo pienso conceptualmente acerca de cómo el orden de mis valores booleanos se transforma de nuevo en los valores originales cuando imprimo la lista ordenada final?

Sorted () es un método incorporado que (hecho curioso ) utiliza un algoritmo de clasificación híbrido llamado Timsortque combina aspectos de clasificación de fusión y clasificación de inserción. Me parece claro que cuando lo llamas, hay una mecánica que guarda estos valores en la memoria y los agrupa con su identidad booleana (máscara) determinada por (...!) La función lambda. El orden está determinado por su identidad booleana calculada a partir de la función lambda, pero tenga en cuenta que estas sublistas (de uno y ceros) no están ordenadas por sus valores originales. Por lo tanto, la lista final, si bien está organizada por Odds and Evens, no está ordenada por sublista (las evens en este caso están fuera de servicio). El hecho de que las probabilidades estén ordenadas es porque ya estaban ordenadas por coincidencia en la lista original. La conclusión de todo esto es que cuando lambda hace esa transformación, se conserva el orden original de las sublistas.

Entonces, ¿cómo se relaciona todo esto con la pregunta original y, lo que es más importante, nuestra intuición sobre cómo debemos implementar sorted () con su argumento clave y lambda?

Esa función lambda puede considerarse como un puntero que apunta a los valores que debemos clasificar, ya sea un puntero que asigna un valor a su valor booleano transformado por la función lambda, o si es un elemento particular en una lista anidada, tupla, dict, etc., nuevamente determinado por la función lambda.

Vamos a intentar y predecir qué sucede cuando ejecuto el siguiente código.

mylist = [(3, 5, 8), (6, 2, 8), ( 2, 9, 4), (6, 8, 5)]
sorted(mylist, key=lambda x: x[1])

Mi sortedllamada obviamente dice: "Por favor ordene esta lista". El argumento clave lo hace un poco más específico al decir que, para cada elemento (x) en mylist, devuelva el índice 1 de ese elemento, luego ordene todos los elementos de la lista original 'mylist' por el orden ordenado de la lista calculado por La función lambda. Como tenemos una lista de tuplas, podemos devolver un elemento indexado de esa tupla. Entonces obtenemos:

[(6, 2, 8), (3, 5, 8), (6, 8, 5), (2, 9, 4)]

Ejecute ese código y verá que este es el orden. Intente indexar una lista de enteros y verá que el código se rompe.

Esta fue una explicación larga, pero espero que esto ayude a 'ordenar' su intuición sobre el uso de funciones lambda como argumento clave en sorted () y más allá.

PaulG
fuente
8
Excelente y comprensiva explicación. Esta respuesta merece 100 puntos. Pero me pregunto por qué los me gusta son menos a esta respuesta.
Javed
3
Gracias por la profunda explicación. Yo leo los documentos y la clasificación de cómo hacerlo y no estaba claro para mí la idea detrás de la keyfunción. Si está tratando de entender la sortedfunción, la lambdasintaxis simplemente se interpone en el camino de la comprensión.
sanbor
3
Esta es la mejor explicación aquí. Esto fue realmente útil para ayudarme a entender cómo funcionaba esto realmente. Comprendí la función lambda, pero usarla en el contexto de sorted () no tenía sentido. Esto realmente ayudó, gracias!
TGWaffles
2
Esta es una respuesta brillante. Saludo señor.
Rajesh Mappu
2
Esta es una de mis respuestas favoritas en el desbordamiento de pila. ¡Gracias!
AdR
26

lambdaes una palabra clave de Python que se usa para generar funciones anónimas .

>>> (lambda x: x+2)(3)
5
Ignacio Vazquez-Abrams
fuente
2
¿Por qué hay paréntesis alrededor de cada uno?
Christopher Markieta el
19
Los parens existen 3porque se pasa a una función. Los parens están alrededor de la lambda para que la expresión no se analice como lambda x: x+2(3), lo que no es válido ya 2que no es una función.
Ignacio Vazquez-Abrams
No estoy seguro de que me guste el término funciones "anónimas". Quiero decir, es cierto que no se nombran, por lo que el anonimato es "técnicamente" preciso. Prefiero referirme a ellos como "funciones temporales". Pero entonces, soy pedante.
user5179531
12

La variableizquierda del :es un nombre de parámetro. El uso de variablea la derecha está haciendo uso del parámetro.

Significa casi exactamente lo mismo que:

def some_method(variable):
  return variable[0]
sblom
fuente
5

Un ejemplo más de uso de la función sorted () con key = lambda. Consideremos que tienes una lista de tuplas. En cada tupla tiene una marca, modelo y peso del automóvil y desea ordenar esta lista de tuplas por marca, modelo o peso. Puedes hacerlo con lambda.

cars = [('citroen', 'xsara', 1100), ('lincoln', 'navigator', 2000), ('bmw', 'x5', 1700)]

print(sorted(cars, key=lambda car: car[0]))
print(sorted(cars, key=lambda car: car[1]))
print(sorted(cars, key=lambda car: car[2]))

Resultados:

[('bmw', 'x5', '1700'), ('citroen', 'xsara', 1100), ('lincoln', 'navigator', 2000)]
[('lincoln', 'navigator', 2000), ('bmw', 'x5', '1700'), ('citroen', 'xsara', 1100)]
[('citroen', 'xsara', 1100), ('bmw', 'x5', 1700), ('lincoln', 'navigator', 2000)]
filler36
fuente
3

lambdaes una función anónima, no una función arbitraria. El parámetro que se acepta sería la variable con la que está trabajando y la columna en la que lo está ordenando.

Makoto
fuente
1

Solo para reformular, la tecla (Opcional. Una función a ejecutar para decidir el orden. El valor predeterminado es Ninguno) en las funciones ordenadas espera una función y usted usa lambda.

Para definir lambda, especifique la propiedad del objeto que desea ordenar y la función ordenada incorporada de Python se encargará automáticamente de ello.

Si desea ordenar por varias propiedades, asigne key = lambda x: (propiedad1, propiedad2).

Para especificar el orden, pase reverse = true como tercer argumento (Opcional. Un booleano. False ordenará ascendente, True ordenará descendente. El valor predeterminado es False) de la función ordenada.

kta
fuente
1

Respuesta simple y que no requiere mucho tiempo con un ejemplo relevante a la pregunta formulada Siga este ejemplo:

 user = [{"name": "Dough", "age": 55}, 
            {"name": "Ben", "age": 44}, 
            {"name": "Citrus", "age": 33},
            {"name": "Abdullah", "age":22},
            ]
    print(sorted(user, key=lambda el: el["name"]))
    print(sorted(user, key= lambda y: y["age"]))

Mire los nombres en la lista, comienzan con D, B, C y A. Y si nota las edades, son 55, 44, 33 y 22. El primer código impreso

print(sorted(user, key=lambda el: el["name"]))

Resultados a:

[{'name': 'Abdullah', 'age': 22}, 
{'name': 'Ben', 'age': 44}, 
{'name': 'Citrus', 'age': 33}, 
{'name': 'Dough', 'age': 55}]

ordena el nombre, porque por clave = lambda el: el ["nombre"] estamos ordenando los nombres y los nombres regresan en orden alfabético.

El segundo código de impresión

print(sorted(user, key= lambda y: y["age"]))

Resultado:

[{'name': 'Abdullah', 'age': 22},
 {'name': 'Citrus', 'age': 33},
 {'name': 'Ben', 'age': 44}, 
 {'name': 'Dough', 'age': 55}]

ordena por edad y, por lo tanto, la lista vuelve por orden ascendente de edad.

Pruebe este código para una mejor comprensión.

AbdullahS96
fuente