Dlaczego dane wyjściowe następujących dwóch składanych list są różne, mimo że funkcja f
i lambda
funkcja są takie same?
f = lambda x: x*x
[f(x) for x in range(10)]
i
[lambda x: x*x for x in range(10)]
Pamiętaj o obu type(f)
i type(lambda x: x*x)
zwróć ten sam typ.
[lambda x: x*x for x in range(10)]
jest szybszy niż pierwszy, ponieważ nie wywołuje wielokrotnie funkcji pętli zewnętrznej f.[x*x for x in range(10)]
jest lepiej.Odpowiedzi:
Pierwsza tworzy pojedynczą funkcję lambda i wywołuje ją dziesięć razy.
Drugi nie wywołuje funkcji. Tworzy 10 różnych funkcji lambda. Umieszcza je wszystkie na liście. Aby uczynić go odpowiednikiem pierwszego, którego potrzebujesz:
Lub jeszcze lepiej:
źródło
map(lambda x: x*x, range(10))
, co prawdopodobnie było tym, co miał na myśli PO.list(map(lambda x: x*x, range(10)))
da ci[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
To pytanie dotyka bardzo śmierdzącej części „słynnej” i „oczywistej” składni Pythona - tego, co ma pierwszeństwo, lambda, czyli for of list compilation.
Nie sądzę, aby celem PO było wygenerowanie listy kwadratów od 0 do 9. Gdyby tak było, moglibyśmy podać jeszcze więcej rozwiązań:
Ale nie o to chodzi. Chodzi o to, że W (hy) TF czy to niejednoznaczne wyrażenie jest tak sprzeczne z intuicją? I na koniec mam dla ciebie idiotyczną sprawę, więc nie odrzucaj mojej odpowiedzi zbyt wcześnie (miałem ją na rozmowie o pracę).
Tak więc zrozumienie OP zwróciło listę lambd:
To oczywiście tylko 10 różnych kopii funkcji do kwadratu, patrz:
Zwróć uwagę na adresy pamięci lambd - wszystkie są różne!
Oczywiście możesz mieć bardziej „optymalną” (haha) wersję tego wyrażenia:
Widzieć? 3 razy ta sama lambda.
Zwróć uwagę, że użyłem
_
jakofor
zmiennej. Nie ma to nic wspólnego zx
wlambda
(jest leksykalnie przyćmione!). Zdobyć?Pomijam dyskusję, dlaczego pierwszeństwo składni nie jest takie, że to wszystko oznaczało:
który mógłby być:,
[[0, 1, 4, ..., 81]]
lub[(0, 1, 4, ..., 81)]
, lub co wydaje mi się najbardziej logiczne , byłby tolist
element 1 -generator
zwracający wartości. Po prostu tak nie jest, język nie działa w ten sposób.ALE co, jeśli ...
A co jeśli NIE przyćmisz
for
zmiennej ORAZ użyjesz jej w swoimlambda
s ???Cóż, wtedy dzieje się bzdury. Spójrz na to:
oznacza to oczywiście:
ALE NIE OZNACZA:
To jest po prostu szalone!
Lambdy w zrozumieniu list są zamknięciem zakresu tego rozumienia. Leksykalny zamknięcie, więc odnoszą się do
i
poprzez odniesienie, a nie jej wartość, gdy oceniano!A więc to wyrażenie:
JEST Z grubsza RÓWNOWAŻNA do:
Jestem pewien, że moglibyśmy zobaczyć więcej tutaj, używając dekompilatora Pythona (przez co mam na myśli np.
dis
Moduł), ale dla dyskusji Python-VM-agnostic to wystarczy. To tyle, jeśli chodzi o pytanie o pracę.A teraz, jak zrobić a
list
z mnożników lambd, które naprawdę mnożą się przez kolejne liczby całkowite? Cóż, podobnie jak zaakceptowana odpowiedź, musimy zerwać bezpośrednie powiązaniei
, zawijając je w innylambda
, który jest wywoływany wewnątrz wyrażenia rozumienia listy:Przed:
Po:
(Miałem również zewnętrzną zmienną lambda =
i
, ale zdecydowałem, że to jest jaśniejsze rozwiązanie - wprowadziłemy
, abyśmy wszyscy mogli zobaczyć, która wiedźma jest którą).Edycja 30.08.2019:
Zgodnie z sugestią @josoler, która jest również obecna w odpowiedzi @sheridp - wartość „zmiennej pętli” rozumienia listy może być „osadzona” wewnątrz obiektu - kluczem jest, aby uzyskać do niej dostęp w odpowiednim czasie. Sekcja „After” powyżej robi to, zawijając ją w inną
lambda
i wywołując ją natychmiast z bieżącą wartościąi
. Innym sposobem (nieco łatwiejszym do odczytania - nie wywołuje efektu „WAT”) jest przechowywanie wartościi
wewnątrzpartial
obiektu i sprawienie, aby „wewnętrzna” (oryginał)lambda
wzięła ją jako argument (przekazany przezpartial
obiekt w czas połączenia), tj .:Po 2:
Świetnie, ale wciąż jest dla Ciebie mały zwrot akcji! Powiedzmy, że nie chcemy ułatwić czytnika kodu i przekazać czynnik według nazwy (jako argument słowa kluczowego do
partial
). Zróbmy trochę zmiany nazwy:Po 2,5:
WAT?
Czekaj ... Zmieniamy liczbę argumentów o 1 i przechodzimy od „za dużo” do „za mało”?
Cóż, to nie jest prawdziwy WAT, gdy mijamy
coef
siępartial
w ten sposób, staje się argumentem kluczowym, więc musi przyjść po pozycyjnymx
argumentu, tak jak poniżej:Po 3:
Wolałbym ostatnią wersję od zagnieżdżonej lambdy, ale dla każdej ich własnej ...
źródło
[partial(lambda i, x: i * x, i) for i in (1, 2)]
Duża różnica polega na tym, że pierwszy przykład faktycznie wywołuje lambdę
f(x)
, a drugi nie.Twój pierwszy przykład jest równoważny,
[(lambda x: x*x)(x) for x in range(10)]
podczas gdy twój drugi przykład jest równoważny[f for x in range(10)]
.źródło
Pierwszy
działa
f()
dla każdej wartości w zakresie, więc działaf(x)
dla każdej wartościDrugie
uruchamia lambdę dla każdej wartości z listy, więc generuje wszystkie te funkcje.
źródło
Ludzie dał dobre odpowiedzi, ale zapomniałem wspomnieć najważniejszą rolę w moim zdaniem: W drugim przykładzie
X
z listy ze zrozumieniem nie jest taka sama jakX
zlambda
funkcji, są całkowicie niezależne. Zatem drugi przykład jest w rzeczywistości taki sam, jak:Wewnętrzne iteracje
range(10)
są odpowiedzialne tylko za utworzenie 10 podobnych funkcji lambda na liście (10 oddzielnych funkcji, ale całkowicie podobnych - zwracanie potęgi 2 z każdego wejścia).Z drugiej strony, pierwszy przykład działa zupełnie inaczej, ponieważ X iteracji DZIAŁA z wynikami, dla każdej iteracji wartość jest
X*X
taka, że wynik byłby[0,1,4,9,16,25, 36, 49, 64 ,81]
źródło
Pozostałe odpowiedzi są poprawne, ale jeśli próbujesz utworzyć listę funkcji, z których każda ma inny parametr, które można wykonać później , zrobi to następujący kod:
Chociaż przykład jest wymyślony, uznałem go za przydatny, gdy chciałem uzyskać listę funkcji, z których każda drukuje coś innego, tj
źródło