Składnia za sortowaniem (klucz = lambda:…)

152

Nie całkiem rozumiem składnię sorted()argumentu:

key=lambda variable: variable[0]

Czy nie jest lambdaarbitralne? Dlaczego jest variablepodane dwukrotnie w tym, co wygląda jak adict ?

Christopher Markieta
źródło

Odpowiedzi:

162

keyto funkcja, która zostanie wywołana w celu przekształcenia elementów kolekcji przed ich porównaniem. Parametr przekazany dokey musi być wywoływalny.

Użycie lambdatworzy anonimową funkcję (którą można wywołać). W przypadku sortedwywoływanego przyjmuje tylko jeden parametr. Python lambdajest dość prosty. Tak naprawdę może zrobić i zwrócić tylko jedną rzecz.

Składnia lambdato słowo, lambdapo którym następuje lista nazw parametrów, a następnie pojedynczy blok kodu. Lista parametrów i blok kodu są oddzielone dwukropkiem. Jest to podobne do innych konstrukcji w Pythonie, jak również takich jak while, for, ifi tak dalej. Wszystkie są instrukcjami, które zazwyczaj zawierają blok kodu. Lambda to po prostu kolejny przykład instrukcji z blokiem kodu.

Możemy porównać użycie lambdy z def przy tworzeniu funkcji.

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

lambda po prostu daje nam sposób na zrobienie tego bez przypisywania nazwy. Co sprawia, że ​​świetnie nadaje się do używania jako parametru funkcji.

variable jest tu używany dwukrotnie, ponieważ po lewej stronie dwukropka jest to nazwa parametru, a po prawej jest używany w bloku kodu do obliczenia czegoś.

Evan
źródło
10
uwaga (do OP): należy unikać przypisywania lambda do nazwy (tj. używania jej w sposób inny niż jako funkcja anonimowa). jeśli zauważysz, że to robisz, prawdopodobnie powinieneś po prostu użyć pliku def.
wim
151

Myślę, że wszystkie odpowiedzi tutaj dość dobrze obejmują rdzeń funkcji lambda w kontekście sort (), jednak nadal czuję, że brakuje opisu prowadzącego do intuicyjnego zrozumienia, więc oto moje dwa centy.

Ze względu na kompletność podam z góry oczywiste: sortowane () zwraca listę posortowanych elementów i czy chcemy posortować w określony sposób lub jeśli chcemy posortować złożoną listę elementów (np. Listy zagnieżdżone lub lista krotek) możemy wywołać argument klucz.

Dla mnie intuicyjne zrozumienie kluczowego argumentu, dlaczego musi być wywoływalny, i użycie lambdy jako (anonimowej) funkcji wywoływalnej, aby to osiągnąć, składa się z dwóch części.

  1. Użycie lamby ostatecznie oznacza, że ​​nie musisz pisać (definiować) całej funkcji, takiej jak ta, której przykład dostarcza sblom . Funkcje lambda są tworzone, używane i natychmiast niszczone - dzięki czemu nie dodają do kodu większej ilości kodu, który zostanie użyty tylko raz. To, jak rozumiem, jest podstawową użytecznością funkcji lambda, a jej zastosowania w takich rolach są szerokie. Jego składnia jest czysto konwencyjna, co jest w istocie naturą składni programowej w ogóle. Naucz się składni i skończ z tym.

Składnia lambda jest następująca:

lambda input_variable (s) : smaczna jedna linijka

na przykład

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. Ideą tego keyargumentu jest to, że powinien on przyjąć zestaw instrukcji, które zasadniczo będą wskazywać funkcję „sort ()” na te elementy listy, według których należy sortować. Kiedy mówi key=, to naprawdę oznacza: Kiedy iteruję po liście po jednym elemencie na raz (tj. Dla e na liście), przekażę bieżący element do funkcji, którą podaję w kluczowym argumencie i użyję tego stworzyć przekształconą listę, która poinformuje mnie o kolejności ostatecznej posortowanej listy.

Sprawdź to:

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

Przykład podstawowy:

sorted(mylist)

[2, 3, 3, 4, 6, 8, 23] # wszystkie liczby są w kolejności od małych do dużych.

Przykład 1:

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

[3, 3, 23, 6, 2, 4, 8] # Czy ten posortowany wynik ma dla Ciebie intuicyjny sens?

Zauważ, że moja funkcja lambda nakazała sortowanemu sprawdzić, czy (e) jest parzyste lub nieparzyste przed sortowaniem.

ALE POCZEKAJ! Możesz (a może powinieneś) zastanawiać się nad dwiema rzeczami - po pierwsze, dlaczego moje szanse są przed moimi wartościami parzystymi (ponieważ moja kluczowa wartość wydaje się wskazywać mojej funkcji sortowania, aby nadawała priorytet parzystym wartościom za pomocą operatora mod in x%2==0). Po drugie, dlaczego moje liczby parzyste są niesprawne? 2 jest przed 6, prawda? Analizując ten wynik, dowiemy się czegoś głębszego o tym, jak działa argument „klucz” sortowany (), szczególnie w połączeniu z anonimową funkcją lambda.

Po pierwsze, zauważysz, że chociaż szanse są przed wydarzeniami parzystymi, same nieparzyste nie są sortowane. Dlaczego to?? Przeczytajmy dokumenty :

Funkcje klawiszy Począwszy od Pythona 2.4, zarówno list.sort (), jak i sortowane () dodały parametr key, aby określić funkcję, która ma być wywołana w każdym elemencie listy przed wykonaniem porównań.

Musimy trochę poczytać między wierszami, ale to nam mówi, że funkcja sort jest wywoływana tylko raz, a jeśli określimy argument klucz, to sortujemy według wartości, na którą wskazuje nam funkcja klucza.

Więc jaki jest przykład z użyciem zwrotu modulo? Wartość logiczna: True == 1, False == 0. Jak więc sort radzi sobie z tym kluczem? Zasadniczo przekształca oryginalną listę na sekwencję jedynek i zer.

[3,6,3,2,4,8,23] staje się [0,1,0,1,1,1,0]

Teraz dokądś zmierzamy. Co otrzymujesz, gdy posortujesz przekształconą listę?

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

Okej, więc teraz wiemy, dlaczego szanse są przed wydarzeniami. Ale następne pytanie brzmi: dlaczego 6 nadal występuje przed 2 na mojej ostatecznej liście? Cóż, to proste - to dlatego, że sortowanie odbywa się tylko raz! tj. te 1 nadal reprezentują oryginalne wartości listy, które znajdują się w swoich oryginalnych pozycjach względem siebie. Ponieważ sortowanie odbywa się tylko raz i nie wywołujemy żadnej funkcji sortowania, aby uporządkować oryginalne wartości parzyste od niskiego do wysokiego, wartości te pozostają w pierwotnej kolejności względem siebie.

Ostatnie pytanie brzmi zatem: Jak myślę koncepcyjnie o tym, jak kolejność moich wartości boolowskich zostanie przekształcona z powrotem do pierwotnych wartości, kiedy wydrukuję ostateczną posortowaną listę?

Sorted () to wbudowana metoda, która (fajny fakt) używa hybrydowego algorytmu sortowania o nazwie Timsortłączący w sobie aspekty sortowania przez scalanie i sortowanie przez wstawianie. Wydaje mi się jasne, że kiedy to nazywasz, istnieje mechanika, która utrzymuje te wartości w pamięci i łączy je z ich logiczną tożsamością (maską) określoną przez (...!) Funkcję lambda. Porządek jest określany przez ich tożsamość logiczną obliczoną z funkcji lambda, ale należy pamiętać, że te listy podrzędne (jedynek i zer) same nie są sortowane według ich oryginalnych wartości. W związku z tym ostateczna lista, chociaż jest zorganizowana według kursów i wartości parzystych, nie jest sortowana według podlisty (w tym przypadku nieparzyste są w kolejności). Fakt, że kursy są uporządkowane, wynika z tego, że przez przypadek były już uporządkowane na pierwotnej liście. Wniosek z tego wszystkiego jest taki, że kiedy lambda dokonuje tej transformacji, pierwotna kolejność podlist zostaje zachowana.

Jak więc to wszystko ma się do pierwotnego pytania, a co ważniejsze, do naszej intuicji, jak powinniśmy zaimplementować sort () z jej kluczowym argumentem i lambdą?

Tę funkcję lambda można traktować jako wskaźnik wskazujący na wartości, według których musimy posortować, niezależnie od tego, czy jest to wskaźnik odwzorowujący wartość na jej wartość logiczną przekształconą przez funkcję lambda, czy też jest to określony element zagnieżdżonej listy, krotki, dict itp., ponownie określane przez funkcję lambda.

Spróbujmy przewidzieć, co się stanie, gdy uruchomię następujący kod.

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

Moje sortedwezwanie mówi oczywiście: „Proszę posortować tę listę”. Kluczowy argument sprawia, że ​​jest to trochę bardziej szczegółowe, mówiąc, że dla każdego elementu (x) w mylist należy zwrócić indeks 1 tego elementu, a następnie posortować wszystkie elementy oryginalnej listy „mylist” według posortowanej kolejności listy obliczonej przez funkcja lambda. Ponieważ mamy listę krotek, możemy zwrócić indeksowany element z tej krotki. Więc otrzymujemy:

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

Uruchom ten kod, a przekonasz się, że to jest kolejność. Spróbuj zindeksować listę liczb całkowitych, a zobaczysz, że kod się zepsuje.

To było rozwlekłe wyjaśnienie, ale mam nadzieję, że pomoże to „uporządkować” Twoją intuicję dotyczącą użycia funkcji lambda jako kluczowego argumentu w sort () i poza nią.

PaulG
źródło
8
doskonałe i wyczerpujące wyjaśnienie. Ta odpowiedź zasługuje na 100 punktów. Ale zastanawiam się, dlaczego polubienia są mniejsze w tej odpowiedzi.
javed
3
Dziękuję za głębokie wyjaśnienie. I czytać dokumenty i sortowania how-to i to nie było dla mnie jasne Ideą keyfunkcji. Jeśli próbujesz zrozumieć sortedfunkcję, lambdaskładnia po prostu przeszkadza w zrozumieniu.
sanbor
3
To jest najlepsze wyjaśnienie tutaj. Pomogło mi to naprawdę zrozumieć, jak to faktycznie działa - w pewnym sensie zrozumiałem funkcję lambda, ale używanie jej w kontekście sortowania () nie miało sensu. To naprawdę pomogło, dzięki!
TGWaffles
2
To świetna odpowiedź. Pozdrawiam pana.
Rajesh Mappu
2
To jedna z moich ulubionych odpowiedzi na temat przepełnienia stosu. Dziękuję Ci!
AdR
26

lambdato słowo kluczowe Pythona używane do generowania anonimowych funkcji .

>>> (lambda x: x+2)(3)
5
Ignacio Vazquez-Abrams
źródło
2
Dlaczego wokół każdego z nich są nawiasy?
Christopher Markieta
19
Pareny są w pobliżu, 3ponieważ są przekazywane do funkcji. Pareny znajdują się wokół lambdy, więc wyrażenie nie jest analizowane jako lambda x: x+2(3), co jest nieprawidłowe, ponieważ 2nie jest funkcją.
Ignacio Vazquez-Abrams
Nie jestem pewien, czy podoba mi się termin „funkcje anonimowe”. To znaczy, to prawda, że ​​nie są wymieniani, więc anonimowość jest „technicznie” dokładna. Wolałbym raczej nazywać je „funkcjami tymczasowymi”. Ale z drugiej strony jestem pedantem.
user5179531
12

Po variablelewej stronie :znajduje się nazwa parametru. Użycie variablepo prawej stronie polega na wykorzystaniu parametru.

Oznacza prawie dokładnie to samo co:

def some_method(variable):
  return variable[0]
sblom
źródło
5

Jeszcze jeden przykład użycia funkcji sortowanej () z kluczem = lambda. Rozważmy, że masz listę krotek. W każdej krotce masz markę, model i wagę samochodu i chcesz posortować tę listę krotek według marki, modelu lub wagi. Możesz to zrobić za pomocą lambdy.

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

Wyniki:

[('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)]
wypełniacz36
źródło
3

lambdajest funkcją anonimową, a nie arbitralną. Akceptowanym parametrem byłaby zmienna, z którą pracujesz, oraz kolumna, według której ją sortujesz.

Makoto
źródło
1

Aby przeformułować, klucz (Opcjonalnie. Funkcja do wykonania w celu ustalenia kolejności. Domyślnie jest to Brak) w posortowanych funkcjach oczekuje funkcji i używasz lambda.

Aby zdefiniować lambdę, określasz właściwość obiektu, którą chcesz sortować, a wbudowana funkcja sortowania Pythona automatycznie się tym zajmie.

Jeśli chcesz sortować według wielu właściwości, przypisz klucz = lambda x: (właściwość1, właściwość2).

Aby określić kolejność, podaj reverse = true jako trzeci argument (Opcjonalne. Wartość logiczna. False - sortowanie rosnąco, True - malejąco. Wartość domyślna to False) posortowanej funkcji.

kta
źródło
1

Prosta i nie czasochłonna odpowiedź z przykładem adekwatnym do zadanego pytania Postępuj zgodnie z tym przykładem:

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

Spójrz na nazwiska na liście, zaczynają się od D, B, C i A. A jeśli zauważysz wiek, są to 55, 44, 33 i 22. Pierwszy wydrukowany kod

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

Wyniki do:

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

sortuje nazwę, ponieważ przez klucz = lambda el: el ["nazwa"] sortujemy nazwy i nazwy zwracane w kolejności alfabetycznej.

Drugi kod wydruku

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

Wynik:

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

sortuje według wieku, a zatem lista jest zwracana rosnąco według wieku.

Wypróbuj ten kod, aby lepiej zrozumieć.

AbdullahS96
źródło