Sintaxis detrás de sorted(key=lambda: ...) [duplicado]

Resuelto Christopher Markieta asked hace 12 años • 10 respuestas

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

key=lambda variable: variable[0]

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

Christopher Markieta avatar Jan 23 '12 09:01 Christopher Markieta
Aceptado

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á mi granito de arena.

Para completar, diré lo obvio desde el principio: 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 función invocable (anónima) para lograrlo se divide en dos partes.

  1. En última instancia, usar lamba significa que no es necesario escribir (definir) una función completa. Las funciones Lambda se crean, usan y destruyen inmediatamente, para que no estropeen su código con más código que solo se usará una vez. Esta, según tengo entendido, es la utilidad principal de la función lambda y su aplicación para dicha función es amplia. Su sintaxis es puramente una convención, que es en esencia la naturaleza de la sintaxis programática en general. Aprenda la sintaxis y termine de una vez.

La sintaxis Lambda es la siguiente:

lambda input_variable(s): tasty one liner

¿ Dónde lambdahay una palabra clave de Python?

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 debería incluir un conjunto de instrucciones que esencialmente apuntarán la función 'sorted()' a aquellos elementos de la lista que deberían usarse para ordenar. Cuando dice key=, lo que realmente significa es: A medida que itero a través de la lista, un elemento a la vez (es decir for e in some_list), pasaré el elemento actual a la función especificada por el argumento clave y lo usaré para crear una transformación. lista que me informará sobre el orden de la lista ordenada final.

Échale un vistazo:

In [6]: mylist = [3, 6, 3, 2, 4, 8, 23]  # an example list
# sorted(mylist, key=HowToSort)  # what we will be doing

Ejemplo básico:

# mylist = [3, 6, 3, 2, 4, 8, 23]
In [7]: sorted(mylist)
Out[7]: [2, 3, 3, 4, 6, 8, 23]  
# all numbers are in ascending order (i.e.from low to high).

Ejemplo 1:

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

# Quick Tip: The % operator returns the *remainder* of a division
# operation. So the key lambda function here is saying "return True 
# if x divided by 2 leaves a remainer of 0, else False". This is a 
# typical way to check if a number is even or odd.

Out[8]: [3, 3, 23, 6, 2, 4, 8]  
# Does this sorted result make intuitive sense to you?

Observe que mi función lambda me indicó sortedque verificara si cada elemento eera par o impar antes de ordenar.

¡PERO ESPERA! Quizás (o quizás deberías) preguntarte dos cosas.

Primero, ¿por qué los números impares van antes que los pares? Después de todo, el valor clave parece indicarle a la sortedfunción que priorice los pares utilizando el modoperador en x % 2 == 0.

En segundo lugar, ¿por qué los números pares siguen desordenados? 2 viene antes del 6, ¿verdad?

Al analizar este resultado, aprenderemos algo más profundo sobre cómo funciona realmente el argumento "clave", especialmente en conjunto con la función lambda anónima.

En primer lugar, notarás que, si bien las probabilidades aparecen antes que los pares, los pares en sí no están ordenados. ¿¿Por qué es esto?? Leamos los documentos :

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

Tenemos que leer un poco entre líneas aquí, 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, entonces ordenamos por el valor al que nos señala esa función clave.

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

[3, 6, 3, 2, 4, 8, 23]se convierte[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 vienen antes que los pares. Pero la siguiente pregunta es: ¿Por qué el 6 todavía aparece antes del 2 en mi lista final? Bueno, eso es fácil: ¡es porque la clasificación solo ocurre una vez! Esos 1 todavía representan los valores de la lista original, que están en sus posiciones originales entre sí . Dado que la clasificación solo ocurre una vez, y no llamamos a ningún tipo de función de clasificación para ordenar los números pares originales de menor a mayor, esos valores permanecen en su orden original entre sí.

La pregunta final es entonces la siguiente: ¿Cómo pienso conceptualmente cómo el orden de mis valores booleanos se transforma nuevamente a los valores originales cuando imprimo la lista ordenada final?

Sorted()es un método incorporado que (dato curioso) utiliza un algoritmo de clasificación híbrido llamado Timsort que combina aspectos de clasificación por fusión y clasificación por inserción. Me parece claro que cuando lo llamas, hay una mecánica que mantiene 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 unos y ceros) no están ordenadas por sus valores originales. Por lo tanto, la lista final, aunque está organizada por pares e impares, no está ordenada por sublista (los pares en este caso están desordenados). El hecho de que las cuotas estén ordenadas es porque ya estaban ordenadas por coincidencia en la lista original. La conclusión de todo esto es que cuando lambda realiza 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, con nuestra intuición sobre cómo debemos implementar sorted()su argumento clave y lambda?

Esa función lambda se puede considerar como un puntero que apunta a los valores por los que necesitamos ordenar, 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.

Intentemos predecir qué sucede cuando ejecuto el siguiente código.

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

Mi sortedllamada obviamente dice: "Ordene esta lista". El argumento clave lo hace un poco más específico al decir, 'para cada elemento xen mylist, devuelva el segundo índice de ese elemento, luego ordene todos los elementos de la lista original mylistsegún el orden de clasificación de la lista calculada por la función lambda. Como tenemos una lista de tuplas, podemos devolver un elemento indexado de esa tupla usando la función lambda.

El puntero que se utilizará para ordenar sería:

[5, 2, 9, 8] # the second element of each tuple

Al ordenar esta lista de punteros se devuelve:

[2, 5, 8, 9]

Aplicando esto a mylist, obtenemos:

Out[10]: [(6, 2, 8), (3, 5, 8), (6, 8, 5), (2, 9, 4)]
# Notice the sorted pointer list is the same as the second index of each tuple in this final list

Ejecute ese código y encontrará que este es el orden. Intente ordenar una lista de números enteros usando esta función clave y encontrará que el código se rompe (¿por qué? Porque, por supuesto, no puede indexar un número entero).

Esta fue una explicación larga, pero espero que ayude a sortsu intuición sobre el uso de lambdafunciones, como argumento clave en sorted()y más allá.

PaulG avatar Mar 23 '2017 03:03 PaulG

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

El uso de lambdacrea una función anónima (que se puede invocar). En el caso del sortedinvocable solo toma un parámetro. El de Python lambdaes bastante simple. En realidad, sólo puede hacer y devolver una cosa.

La sintaxis de lambdaes la palabra lambdaseguida de la lista de nombres de parámetros y luego un único bloque de código. La lista de parámetros y el bloque de código están delimitados por dos puntos. Esto es similar a otras construcciones en Python , como while, etc. Todas son declaraciones que normalmente tienen un bloque de código. Lambda es sólo otra instancia de una declaración con un bloque de código.forif

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 brinda una manera de hacer esto sin asignar un nombre. Lo que lo hace ideal para usarlo como parámetro de una función.

variablese usa dos veces aquí porque en el lado izquierdo de los dos puntos está el nombre de un parámetro y en el lado derecho se usa en el bloque de código para calcular algo.

Evan avatar Jan 23 '2012 02:01 Evan