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ń:
squares = []
for x in range(10): squares.append(x*x)
- to jest stary dobry sposób imperatywnej składni.
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:
[(lambda x: x*x) for x in range(10)]
To oczywiście tylko 10 różnych kopii funkcji do kwadratu, patrz:
>>> [lambda x: x*x for _ in range(3)]
[<function <lambda> at 0x00000000023AD438>, <function <lambda> at 0x00000000023AD4A8>, <function <lambda> at 0x00000000023AD3C8>]
Zwróć uwagę na adresy pamięci lambd - wszystkie są różne!
Oczywiście możesz mieć bardziej „optymalną” (haha) wersję tego wyrażenia:
>>> [lambda x: x*x] * 3
[<function <lambda> at 0x00000000023AD2E8>, <function <lambda> at 0x00000000023AD2E8>, <function <lambda> at 0x00000000023AD2E8>]
Widzieć? 3 razy ta sama lambda.
Zwróć uwagę, że użyłem _jako forzmiennej. Nie ma to nic wspólnego z xw lambda(jest leksykalnie przyćmione!). Zdobyć?
Pomijam dyskusję, dlaczego pierwszeństwo składni nie jest takie, że to wszystko oznaczało:
[lambda x: (x*x for x in range(10))]
który mógłby być:, [[0, 1, 4, ..., 81]]lub [(0, 1, 4, ..., 81)], lub co wydaje mi się najbardziej logiczne , byłby to listelement 1 - generatorzwracają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 forzmiennej ORAZ użyjesz jej w swoim lambdas ???
Cóż, wtedy dzieje się bzdury. Spójrz na to:
[lambda x: x * i for i in range(4)]
oznacza to oczywiście:
[(lambda x: x * i) for i in range(4)]
ALE NIE OZNACZA:
[(lambda x: x * 0), (lambda x: x * 1), ... (lambda x: x * 3)]
To jest po prostu szalone!
Lambdy w zrozumieniu list są zamknięciem zakresu tego rozumienia. Leksykalny zamknięcie, więc odnoszą się do ipoprzez odniesienie, a nie jej wartość, gdy oceniano!
A więc to wyrażenie:
[(lambda x: x * i) for i in range(4)]
JEST Z grubsza RÓWNOWAŻNA do:
[(lambda x: x * 3), (lambda x: x * 3), ... (lambda x: x * 3)]
Jestem pewien, że moglibyśmy zobaczyć więcej tutaj, używając dekompilatora Pythona (przez co mam na myśli np. disModuł), ale dla dyskusji Python-VM-agnostic to wystarczy. To tyle, jeśli chodzi o pytanie o pracę.
A teraz, jak zrobić a listz mnożników lambd, które naprawdę mnożą się przez kolejne liczby całkowite? Cóż, podobnie jak zaakceptowana odpowiedź, musimy zerwać bezpośrednie powiązanie i, zawijając je w inny lambda, który jest wywoływany wewnątrz wyrażenia rozumienia listy:
Przed:
>>> a = [(lambda x: x * i) for i in (1, 2)]
>>> a[1](1)
2
>>> a[0](1)
2
Po:
>>> a = [(lambda y: (lambda x: y * x))(i) for i in (1, 2)]
>>> a[1](1)
2
>>> a[0](1)
1
(Miałem również zewnętrzną zmienną lambda = i, ale zdecydowałem, że to jest jaśniejsze rozwiązanie - wprowadziłem y, 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ą lambdai 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ści iwewnątrz partialobiektu i sprawienie, aby „wewnętrzna” (oryginał) lambdawzięła ją jako argument (przekazany przez partialobiekt w czas połączenia), tj .:
Po 2:
>>> from functools import partial
>>> a = [partial(lambda y, x: y * x, i) for i in (1, 2)]
>>> a[0](2), a[1](2)
(2, 4)
Ś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:
>>> a = [partial(lambda coef, x: coef * x, coef=i) for i in (1, 2)]
>>> a[0](1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: <lambda>() got multiple values for argument 'coef'
WAT?
>>> a[0]()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: <lambda>() missing 1 required positional argument: 'x'
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 coefsię partialw ten sposób, staje się argumentem kluczowym, więc musi przyjść po pozycyjnym xargumentu, tak jak poniżej:
Po 3:
>>> a = [partial(lambda x, coef: coef * x, coef=i) for i in (1, 2)]
>>> a[0](2), a[1](2)
(2, 4)
Wolałbym ostatnią wersję od zagnieżdżonej lambdy, ale dla każdej ich własnej ...
[lambda x: x*x for x in range(10)]jest szybszy niż pierwszy, ponieważ nie wywołuje wielokrotnie funkcji pętli zewnętrznej f.