Liste anlamalarında Lambda işlevi


156

Aşağıdaki iki liste anlayışının çıktısı neden fve lambdaişlev aynı olsa da farklı ?

f = lambda x: x*x
[f(x) for x in range(10)]

ve

[lambda x: x*x for x in range(10)]

İkinize de dikkat edin type(f)ve type(lambda x: x*x)aynı tipte dönün.


[lambda x: x*x for x in range(10)]bir dış döngü işlevini, f'yi tekrar tekrar çağırmadığı için ilkinden daha hızlıdır.
riza

@Selinap: ... hayır, bunun yerine döngü boyunca her seferinde yeni bir işlev yaratan bir marka yaratıyorsunuz. ... ve bu yeni işlevi yaratmanın ek yükü, sonra arama biraz daha yavaştır (yine de benim sistemimde).
Gerrat

@Gerrat: Ek yükte bile daha hızlı. Ama elbette [x*x for x in range(10)]daha iyi.
riza

35
Google foobar erişimini almak için buraya yeni girdim :)
Gal Margalit

Yanıtlar:


275

İlki tek bir lambda işlevi oluşturur ve onu on kez çağırır.

İkincisi, işlevi çağırmaz. 10 farklı lambda işlevi oluşturur. Bunların hepsini bir listeye koyar. İhtiyacınız olan ilke eşdeğer hale getirmek için:

[(lambda x: x*x)(x) for x in range(10)]

Veya daha iyisi:

[x*x for x in range(10)]

13
Ya map(lambda x: x*x, range(10))da muhtemelen OP'nin ilk başta kastettiği buydu.
Daniel Roseman

evet, lambda x: x * x .. (x) ilke gibi görünüyor.
staticor

[lambda x: x * x (10) aralığında x için] temelde haskell'deki bir işlevdir
moshe beeri

1
@DanielRoseman veya hassas olmak list(map(lambda x: x*x, range(10)))size verecek[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
rrlamichhane

119

Bu soru, "ünlü" ve "aşikar" Python sözdiziminin çok kötü bir parçasına dokunuyor - öncelikli olan, lambda veya liste kavrayışı için.

OP'nin amacının 0'dan 9'a kadar bir kareler listesi oluşturmak olduğunu sanmıyorum. Durum böyleyse, daha da fazla çözüm verebiliriz:

squares = []
for x in range(10): squares.append(x*x)
  • bu zorunlu sözdiziminin iyi bir yoludur.

Ama konu bu değil. Buradaki nokta W (hy) TF, bu belirsiz ifade bu kadar sezgisel mi? Ve sonunda senin için aptalca bir davam var, bu yüzden cevabımı çok erken reddetme (bunu bir iş görüşmesinde almıştım).

Öyleyse, OP'nin anlayışı lambdaların bir listesini verdi:

[(lambda x: x*x) for x in range(10)]

Bu elbette , kare alma fonksiyonunun sadece 10 farklı kopyasıdır, bakınız:

>>> [lambda x: x*x for _ in range(3)]
[<function <lambda> at 0x00000000023AD438>, <function <lambda> at 0x00000000023AD4A8>, <function <lambda> at 0x00000000023AD3C8>]

Not lambdas bellek adreslerini - hepsi farklıdır!

Elbette bu ifadenin daha "optimal" (haha) bir versiyonuna sahip olabilirsiniz:

>>> [lambda x: x*x] * 3
[<function <lambda> at 0x00000000023AD2E8>, <function <lambda> at 0x00000000023AD2E8>, <function <lambda> at 0x00000000023AD2E8>]

Görmek? 3 kez aynı lambda.

Lütfen değişken _olarak kullandığımı unutmayın for. O ile ilgisi yoktur xyılında lambda(o lexically gölgelenir!). Anla?

Tartışmayı dışarıda bırakıyorum, sözdizimi önceliğinin neden böyle olmadığı, tüm bunların anlamı:

[lambda x: (x*x for x in range(10))]

hangisi olabilir: [[0, 1, 4, ..., 81]]veya [(0, 1, 4, ..., 81)], ya da en mantıklı bulduğum şey , bu list1 öğeden bir - generatordeğerleri döndüren bir öğe olabilir . Durum böyle değil, dil bu şekilde çalışmıyor.

AMA Ne, Eğer ...

Ya fordeğişkeni gölgelemezseniz VE onu kendi değişkenlerinizde kullanırsanız lambda???

Öyleyse saçmalık olur. Şuna bak:

[lambda x: x * i for i in range(4)]

bu tabii ki:

[(lambda x: x * i) for i in range(4)]

AMA şu anlama gelmez:

[(lambda x: x * 0), (lambda x: x * 1), ... (lambda x: x * 3)]

Bu sadece çılgınlık!

Liste kavrayışındaki lambdalar, bu anlayışın kapsamı üzerinde bir kapanıştır. Bir sözcük kapatma, bu yüzden bakın ireferans yoluyla değil, onun değerini onlar değerlendirildiğinde!

Yani bu ifade:

[(lambda x: x * i) for i in range(4)]

Aşağıdakilere kabaca EŞDEĞER:

[(lambda x: x * 3), (lambda x: x * 3), ... (lambda x: x * 3)]

Eminim burada bir python çözücü kullanarak daha fazlasını görebiliriz (yani dismodülden bahsediyorum), ancak Python-VM-agnostik tartışma için bu yeterlidir. İş görüşmesi sorusu için çok fazla.

Şimdi, listgerçekten ardışık tamsayılarla çarpılan çarpan lambdalar nasıl yapılır ? Pekala, kabul edilen cevaba benzer şekilde, doğrudan bağı bir ibaşkasına sararak kırmamız gerekiyor lambda, ki bu da liste anlama ifadesi içinde çağrılıyor :

Önce:

>>> a = [(lambda x: x * i) for i in (1, 2)]
>>> a[1](1)
2
>>> a[0](1)
2

Sonra:

>>> a = [(lambda y: (lambda x: y * x))(i) for i in (1, 2)]
>>> a[1](1)
2
>>> a[0](1)
1

(Dış lambda değişkenine de sahiptim = i, ama bunun daha net bir çözüm olduğuna karar verdim - yhangi cadının hangisi olduğunu hepimiz görebilmemiz için tanıttım).

Düzenleme 2019-08-30:

@Sheridp tarafından verilen bir yanıtta da bulunan @josoler tarafından yapılan bir öneriyi takiben - liste kavrama "döngü değişkeni" değeri bir nesnenin içine "gömülebilir" - anahtar, doğru zamanda erişilebilmesi içindir. Yukarıdaki "Sonra" bölümü, bunu başka bir bölüme sarıp lambdahemen mevcut değeriyle çağırarak yapar i. Başka bir yol (biraz daha kolay okunur - 'WAT' etkisi üretmez), ibir partialnesnenin içindeki değeri depolamak ve "iç" (orijinal) lambdaonu bir argüman olarak almaktır ( partialnesne tarafından görüşmenin zamanı), yani:

2'den sonra:

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

Harika, ama sizin için hala küçük bir değişiklik var! Diyelim ki kod okuyucuyu kolaylaştırmak ve faktörü isme göre (bir anahtar kelime argümanı olarak partial) iletmek istemiyoruz . Biraz yeniden adlandıralım:

2.5'ten sonra:

>>> 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'

Bekle ... Argüman sayısını 1 değiştirip "çok fazla" dan "çok az" a mı geçiyoruz?

Eh, gerçek bir WAT biz geçerken, değil coefiçin partialbu şekilde o pozisyonel sonra gelmelidir nedenle, bir anahtar kelime argüman haline gelir xşöyle, argüman:

3'ten sonra:

>>> a = [partial(lambda x, coef: coef * x, coef=i) for i in (1, 2)]
>>> a[0](2), a[1](2)
(2, 4)

İç içe yerleştirilmiş lambda yerine son versiyonu tercih ederim, ama her biri kendi ...

Düzenleme 2020-08-18:

Yorumcu dasWesen sayesinde, bu bilgilerin Python belgelerinde yer aldığını öğrendim: https://docs.python.org/3.4/faq/programming.html#why-do-lambdas-defined-in-a-loop- with-different-values-all-return-the-same-result - liste anlamaları yerine döngülerle ilgilenir, ancak fikir aynıdır - lambda işlevinde küresel veya yerel olmayan değişken erişimi. Bir çözüm bile var - varsayılan argüman değerlerini kullanarak (herhangi bir işlev için olduğu gibi):

>>> a = [lambda x, coef=i: coef * x for i in (1, 2)]
>>> a[0](2), a[1](2)
(2, 4)

Bu yolla katsayı değeri, işlev tanımı sırasında i'nin değerine bağlanır (bkz. James Powell'ın "Yukarıdan Aşağıya, Soldan Sağa", aynı zamanda değişken varsayılan değerlerin neden uzak durduğunu da açıklayan konuşmasına bakın).


26
bu acımasız ve alışılmadık bir iş görüşmesi sorusu.
szeitlin

1
Meslektaşım
sormasaydı

9
Vay. Bu saçma davranıştan fena ısırıldım. Gönderiniz için teşekkürler!
Karınca

1
Mükemmel cevap. Ben de bu sorunla karşılaştım. Bir yandan bir Python sınırlamasına işaret ediyor, ancak diğer yandan bir kod kokusu göstergesi de olabilir. Bu çözümü bir oyuncak projesi için kullanıyorum, ancak bu bir üretim ortamında yeniden yapılanmanın bir işareti olabilir.
ahota

2
Açıklık ve eksiksizlik adına son liste anlayışını şöyle yazabilirsiniz:[partial(lambda i, x: i * x, i) for i in (1, 2)]
josoler

19

En büyük fark, ilk örneğin aslında lambda'yı çağırırken f(x), ikinci örneğin yapmamasıdır.

İlk örneğiniz, [(lambda x: x*x)(x) for x in range(10)]ikinci örneğinizin eşdeğeridir [f for x in range(10)].


11

İlki

f = lambda x: x*x
[f(x) for x in range(10)]

ishal f()öyle böylece aralığında her değer için f(x)her değer için

ikinci olan

[lambda x: x*x for x in range(10)]

listedeki her değer için lambda çalıştırır, böylece tüm bu işlevleri üretir.


11

İnsanlar iyi cevaplar verdi ama bence en önemli kısmı söylemeyi unutmuşum: İkinci örnekte Xliste anlama aynı DEĞİLDİR Xait lambdafonksiyonu, bunlar tamamen alakasız. Yani ikinci örnek aslında şununla aynıdır:

[Lambda X: X*X for I in range(10)]

Dahili yinelemeler range(10), yalnızca bir listede 10 benzer lambda işlevi oluşturmaktan sorumludur (10 ayrı işlev ancak tamamen benzer - her girişin gücünü 2 döndürmek).

Öte yandan, ilk örnek tamamen farklı çalışır, çünkü yinelemelerin X'i sonuçlarla etkileşime girer, her yinelemede değer, X*Xsonuç şu şekilde olur:[0,1,4,9,16,25, 36, 49, 64 ,81]


Bu önemli bir noktadır. Size oy verdim ve cevabımda bunu detaylandırdım.
Tomasz Gandor

7

Diğer cevaplar doğrudur, ancak her biri farklı bir parametreye sahip olan ve daha sonra yürütülebilecek bir işlevler listesi yapmaya çalışıyorsanız , aşağıdaki kod bunu yapacaktır:

import functools
a = [functools.partial(lambda x: x*x, x) for x in range(10)]

b = []
for i in a:
    b.append(i())

In [26]: b
Out[26]: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Örnek oluşturulmuş olsa da, her biri farklı bir şeyler yazdıran işlevlerin bir listesini istediğimde yararlı buldum.

import functools
a = [functools.partial(lambda x: print(x), x) for x in range(10)]

for i in a:
    i()
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.