Syntaxe derrière sorted (key = lambda:…)

152

Je ne comprends pas très bien la syntaxe derrière l' sorted()argument:

key=lambda variable: variable[0]

N'est-ce pas lambdaarbitraire? Pourquoi est-il variableindiqué deux fois dans ce qui ressemble à un dict?

Christopher Markieta
la source

Réponses:

164

keyest une fonction qui sera appelée pour transformer les éléments de la collection avant leur comparaison. Le paramètre passé à keydoit être quelque chose qui peut être appelé.

L'utilisation de lambdacrée une fonction anonyme (qui peut être appelée). Dans le cas de sortedl'appelable ne prend qu'un seul paramètre. Python lambdaest assez simple. Il ne peut faire et renvoyer qu'une seule chose en réalité.

La syntaxe de lambdaest le mot lambdasuivi de la liste des noms de paramètres puis d'un seul bloc de code. La liste des paramètres et le bloc de code sont délimités par deux points. Ceci est similaire à d' autres constructions en python aussi bien comme while, for, ifet ainsi de suite. Ce sont toutes des instructions qui ont généralement un bloc de code. Lambda n'est qu'une autre instance d'une instruction avec un bloc de code.

On peut comparer l'utilisation de lambda avec celle de def pour créer une fonction.

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

lambda nous donne simplement un moyen de faire cela sans attribuer de nom. Ce qui le rend idéal pour une utilisation en tant que paramètre d'une fonction.

variable est utilisé deux fois ici car sur la gauche du signe deux-points, c'est le nom d'un paramètre et sur le côté droit, il est utilisé dans le bloc de code pour calculer quelque chose.

Evan
la source
11
note (à OP): il faut éviter d'attribuer un lambda à un nom (c'est-à-dire de l'utiliser d'une manière autre que comme fonction anonyme). si vous vous trouvez en train de faire cela, vous devriez probablement simplement utiliser un fichier def.
wim
151

Je pense que toutes les réponses ici couvrent assez bien ce que fait la fonction lambda dans le contexte de sorted (), mais j'ai toujours l'impression qu'une description qui conduit à une compréhension intuitive fait défaut, alors voici mes deux cents.

Par souci d'exhaustivité, je vais énoncer l'évidence dès le départ: sorted () renvoie une liste d'éléments triés et si nous voulons trier d'une manière particulière ou si nous voulons trier une liste complexe d'éléments (par exemple des listes imbriquées ou une liste de tuples), nous pouvons invoquer l'argument clé.

Pour moi, la compréhension intuitive de l'argument clé, pourquoi il doit être appelable et l'utilisation de lambda comme fonction appelable (anonyme) pour accomplir cela se décompose en deux parties.

  1. En fin de compte, l'utilisation de lamba signifie que vous n'avez pas à écrire (définir) une fonction entière, comme celle que sblom a fournie en exemple. Les fonctions Lambda sont créées, utilisées et immédiatement détruites - de sorte qu'elles ne remplissent pas votre code avec plus de code qui ne sera utilisé qu'une seule fois. Tel que je le comprends, c'est l'utilité de base de la fonction lambda et ses applications pour de tels rôles sont larges. Sa syntaxe est purement conventionnelle, ce qui est essentiellement la nature de la syntaxe programmatique en général. Apprenez la syntaxe et en avez fini avec elle.

La syntaxe Lambda est la suivante:

lambda input_variable (s) : savoureux une doublure

par exemple

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. L'idée derrière l' keyargument est qu'il devrait prendre un ensemble d'instructions qui pointeront essentiellement la fonction 'sorted ()' sur les éléments de liste qui devraient être utilisés pour trier. Quand il dit key=, ce que cela signifie vraiment est: En parcourant la liste un élément à la fois (c'est-à-dire pour e dans la liste), je vais passer l'élément actuel à la fonction que je fournis dans l'argument clé et l'utiliser pour créer une liste transformée qui m'informera de l'ordre de la liste triée finale.

Vérifiez-le:

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

Exemple de base:

sorted(mylist)

[2, 3, 3, 4, 6, 8, 23] # tous les nombres sont dans l'ordre du petit au grand.

Exemple 1:

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

[3, 3, 23, 6, 2, 4, 8] # Ce résultat trié a-t-il un sens intuitif pour vous?

Notez que ma fonction lambda a dit à trié de vérifier si (e) était pair ou impair avant le tri.

MAIS ATTENDEZ! Vous vous demandez peut-être (ou devriez peut-être) deux choses - d'abord, pourquoi mes chances viennent-elles avant mes evens (puisque ma valeur clé semble indiquer à ma fonction triée de prioriser les evens en utilisant l'opérateur mod in x%2==0). Deuxièmement, pourquoi mes evens sont-ils hors service? 2 vient avant 6, non? En analysant ce résultat, nous apprendrons quelque chose de plus profond sur le fonctionnement de l'argument 'key' sorted (), en particulier en conjonction avec la fonction lambda anonyme.

Tout d'abord, vous remarquerez que même si les probabilités précèdent les vents, les vents eux-mêmes ne sont pas triés. Pourquoi est-ce?? Permet de lire les documents :

Fonctions clés À partir de Python 2.4, list.sort () et sorted () ont ajouté un paramètre clé pour spécifier une fonction à appeler sur chaque élément de la liste avant de faire des comparaisons.

Nous devons faire un peu de lecture entre les lignes ici, mais ce que cela nous dit, c'est que la fonction de tri n'est appelée qu'une seule fois, et si nous spécifions l'argument clé, nous trions par la valeur vers laquelle la fonction clé nous indique.

Alors, qu'est-ce que l'exemple utilisant un retour modulo? Une valeur booléenne: True == 1, False == 0. Alors, comment trié traite-t-il cette clé? Il transforme essentiellement la liste d'origine en une séquence de 1 et de 0.

[3,6,3,2,4,8,23] devient [0,1,0,1,1,1,0]

Maintenant, nous arrivons quelque part. Qu'obtenez-vous lorsque vous triez la liste transformée?

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

D'accord, alors maintenant nous savons pourquoi les chances passent avant les égales. Mais la question suivante est: pourquoi le 6 vient-il toujours avant le 2 dans ma liste finale? Eh bien, c'est facile - c'est parce que le tri ne se produit qu'une seule fois! c'est-à-dire que ces 1 représentent toujours les valeurs de liste d'origine, qui sont dans leurs positions d'origine les unes par rapport aux autres. Étant donné que le tri ne se produit qu'une seule fois et que nous n'appelons aucun type de fonction de tri pour ordonner les valeurs paires d'origine de bas en haut, ces valeurs restent dans leur ordre d'origine les unes par rapport aux autres.

La dernière question est alors la suivante: comment est-ce que je pense conceptuellement à la façon dont l'ordre de mes valeurs booléennes est transformé en valeurs d'origine lorsque j'imprime la liste triée finale?

Sorted () est une méthode intégrée qui (fait amusant) utilise un algorithme de tri hybride appelé Timsortqui combine les aspects du tri par fusion et du tri par insertion. Il me semble clair que lorsque vous l'appelez, il y a un mécanisme qui garde ces valeurs en mémoire et les regroupe avec leur identité booléenne (masque) déterminée par (...!) La fonction lambda. L'ordre est déterminé par leur identité booléenne calculée à partir de la fonction lambda, mais gardez à l'esprit que ces sous-listes (de un et de zéros) ne sont pas elles-mêmes triées par leurs valeurs d'origine. Par conséquent, la liste finale, bien qu'organisée par Odds and Evens, n'est pas triée par sous-liste (les evens dans ce cas sont dans le désordre). Le fait que les cotes soient ordonnées est parce qu'elles étaient déjà dans l'ordre par coïncidence dans la liste d'origine. Ce qu'il faut retenir de tout cela, c'est que lorsque lambda effectue cette transformation, l'ordre d'origine des sous-listes est conservé.

Alors, comment tout cela se rapporte-t-il à la question d'origine et, plus important encore, à notre intuition sur la façon dont nous devrions implémenter sorted () avec son argument clé et lambda?

Cette fonction lambda peut être considérée comme un pointeur qui pointe vers les valeurs sur lesquelles nous devons trier, qu'il s'agisse d'un pointeur mappant une valeur à son booléen transformé par la fonction lambda, ou s'il s'agit d'un élément particulier dans une liste imbriquée, un tuple, dict, etc., à nouveau déterminé par la fonction lambda.

Essayons de prédire ce qui se passe lorsque j'exécute le code suivant.

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

Mon sortedappel dit évidemment: "Veuillez trier cette liste". L'argument clé rend cela un peu plus précis en disant, pour chaque élément (x) dans mylist, renvoyer l'index 1 de cet élément, puis trier tous les éléments de la liste d'origine 'mylist' par l'ordre trié de la liste calculée par la fonction lambda. Puisque nous avons une liste de tuples, nous pouvons renvoyer un élément indexé à partir de ce tuple. On obtient donc:

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

Exécutez ce code et vous constaterez que c'est l'ordre. Essayez d'indexer une liste d'entiers et vous constaterez que le code se brise.

Ce fut une longue explication, mais j'espère que cela vous aidera à «trier» votre intuition sur l'utilisation des fonctions lambda comme argument clé dans sorted () et au-delà.

PaulG
la source
8
excellente et complète explication. Cette réponse mérite 100 points. Mais je me demande pourquoi les likes sont moins à cette réponse.
javed le
3
Merci pour l'explication approfondie. J'ai lu les documents et les instructions de tri et l'idée derrière la keyfonction n'était pas claire . Si vous essayez de comprendre la sortedfonction, la lambdasyntaxe ne fait que vous empêcher de comprendre.
sanbor
3
C'est la meilleure explication ici. C'était vraiment utile pour m'aider à comprendre comment cela fonctionnait réellement - j'ai en quelque sorte saisi la fonction lambda, mais son utilisation dans le contexte de sorted () n'avait aucun sens. Cela a vraiment aidé, merci!
TGWaffles
2
C'est une réponse brillante. Je vous salue monsieur.
Rajesh Mappu
2
C'est l'une de mes réponses préférées sur le débordement de pile. Je vous remercie!
AdR
26

lambdaest un mot-clé Python utilisé pour générer des fonctions anonymes .

>>> (lambda x: x+2)(3)
5
Ignacio Vazquez-Abrams
la source
2
Pourquoi y a-t-il des parenthèses autour de chacun?
Christopher Markieta
19
Les parents sont là 3parce qu'ils sont passés à une fonction. Les parens sont autour du lambda afin que l'expression ne soit pas analysée comme lambda x: x+2(3), ce qui n'est pas valide car 2n'est pas une fonction.
Ignacio Vazquez-Abrams
Je ne suis pas sûr d’aimer le terme «fonctions anonymes». Je veux dire, c'est vrai qu'ils ne sont pas nommés, donc anonyme est "techniquement" exact. Je les appellerais plutôt des «fonctions temporaires». Mais alors, je suis un pédant.
user5179531
12

La variablegauche de :est un nom de paramètre. L'utilisation de variablesur la droite utilise le paramètre.

Signifie presque exactement la même chose que:

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

Un autre exemple d'utilisation de la fonction sorted () avec key = lambda. Considérons que vous avez une liste de tuples. Dans chaque tuple, vous avez une marque, un modèle et un poids de la voiture et vous souhaitez trier cette liste de tuples par marque, modèle ou poids. Vous pouvez le faire avec 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]))

Résultats:

[('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)]
remplisseur36
la source
3

lambdaest une fonction anonyme, pas une fonction arbitraire. Le paramètre accepté serait la variable avec laquelle vous travaillez et la colonne dans laquelle vous le triez.

Makoto
la source
1

Juste pour reformuler, la clé (facultative. Une fonction à exécuter pour décider de l'ordre. La valeur par défaut est Aucune) dans les fonctions triées attend une fonction et vous utilisez lambda.

Pour définir lambda, vous spécifiez la propriété d'objet que vous souhaitez trier et la fonction triée intégrée de python s'en chargera automatiquement.

Si vous souhaitez trier par plusieurs propriétés, attribuez key = lambda x: (propriété1, propriété2).

Pour spécifier l'ordre par ordre, passez reverse = true comme troisième argument (facultatif. Une valeur booléenne. False triera par ordre croissant, True triera par ordre décroissant. La valeur par défaut est False) de la fonction triée.

kta
la source
1

Réponse simple et rapide avec un exemple correspondant à la question posée Suivez cet exemple:

 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"]))

Regardez les noms dans la liste, ils commencent par D, B, C et A. Et si vous remarquez les âges, ils sont 55, 44, 33 et 22. Le premier code d'impression

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

Résultats à:

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

trie le nom, car par key = lambda el: el ["name"] nous trions les noms et les noms retournés par ordre alphabétique.

Le deuxième code d'impression

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

Résultat:

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

trie par âge, et donc la liste revient par ordre croissant d'âge.

Essayez ce code pour une meilleure compréhension.

AbdullahS96
la source