Pertanyaan Python: fungsi Lambda dalam Daftar Pemahaman


Mengapa output dari dua daftar pemahaman berikut berbeda, meskipun f dan fungsi lambda adalah sama?

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

dan

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

Pikiran Anda, baik tipe (f) dan tipe (lambda x: x * x) mengembalikan tipe yang sama.


75
2018-05-20 18:39


asal


Jawaban:


Yang pertama membuat fungsi lambda tunggal dan menyebutnya sepuluh kali.

Yang kedua tidak memanggil fungsi. Ini menciptakan 10 fungsi lambda yang berbeda. Itu menempatkan semua itu dalam daftar. Untuk membuatnya setara dengan yang pertama Anda butuhkan:

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

Atau lebih baik lagi:

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

153
2018-05-20 18:41



Pertanyaan ini menyentuh bagian yang sangat menyebalkan dari sintaksis Python yang "terkenal" dan "jelas" - yang lebih diutamakan, lambda, atau untuk pemahaman daftar.

Saya tidak berpikir tujuan OP adalah untuk menghasilkan daftar kuadrat dari 0 hingga 9. Jika itu terjadi, kita bisa memberikan lebih banyak solusi:

squares = []
for x in range(10): squares.append(x*x)
  • ini adalah cara sintaks imperatif yang baik.

Tapi bukan itu intinya. Intinya adalah W (hy) TF adalah ekspresi ambigu ini sehingga kontra-intuitif? Dan saya memiliki kasus bodoh untuk Anda pada akhirnya, jadi jangan remehkan jawaban saya terlalu dini (saya punya wawancara kerja).

Jadi, pemahaman OP mengembalikan daftar lambdas:

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

Ini tentu saja hanya 10 berbeda salinan fungsi kuadrat, lihat:

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

Catatan alamat memori lambdas - mereka semua berbeda!

Anda tentu saja dapat memiliki versi yang lebih "optimal" (haha) dari ekspresi ini:

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

Lihat? 3 kali sama lambda.

Harap dicatat, yang saya gunakan _ sebagai for variabel. Ini tidak ada hubungannya dengan x dalam lambda (Itu dibayangi leksikal!). Mendapatkan?

Saya meninggalkan diskusi, mengapa tidak ada yang lebih penting, bahwa itu semua berarti:

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

yang bisa jadi: [[0, 1, 4, ..., 81]], atau [(0, 1, 4, ..., 81)], atau yang menurut saya paling logis, ini akan menjadi list dari 1 elemen - a generator mengembalikan nilai-nilai. Bukan hanya masalahnya, bahasa tidak berfungsi dengan cara ini.

TAPI Bagaimana jika...

Bagaimana jika Anda TIDAK membayangi for variabel, DAN menggunakannya dalam lambdas ???

Yah, lalu omong kosong terjadi. Lihat ini:

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

ini berarti tentu saja:

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

TAPI itu TIDAK berarti:

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

Ini gila sekali!

Lambdas dalam daftar pemahaman adalah penutupan atas ruang lingkup pemahaman ini. SEBUAH leksikal penutupan, jadi mereka mengacu pada i melalui referensi, dan bukan nilainya ketika mereka dievaluasi!

Jadi, ungkapan ini:

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

IS kira-kira SETIAP UNTUK:

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

Saya yakin kita bisa melihat lebih banyak di sini menggunakan python decompiler (yang saya maksud misalnya dis modul), tetapi untuk diskusi Python-VM-agnostik ini sudah cukup. Begitu banyak pertanyaan wawancara pekerjaan.

Sekarang, bagaimana cara membuat list multiplier lambdas, yang benar-benar berkembang biak dengan bilangan bulat berturut-turut? Nah, sama dengan jawaban yang diterima, kita perlu memutuskan hubungan langsung i dengan membungkusnya dengan yang lain lambda, yang dipanggil dalam ekspresi pemahaman daftar:

Sebelum:

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

Setelah:

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

(Saya memiliki variabel lambda luar juga = i, tetapi saya memutuskan ini adalah solusi yang lebih jelas - saya memperkenalkan y sehingga kita semua bisa melihat penyihir mana yang mana).


47
2017-12-01 13:14



Perbedaan besar adalah bahwa contoh pertama sebenarnya memanggil lambda f(x), sedangkan contoh kedua tidak.

Contoh pertama Anda setara dengan [(lambda x: x*x)(x) for x in range(10)] sedangkan contoh kedua Anda setara dengan [f for x in range(10)].


17
2018-05-20 18:41



Yang pertama

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

berjalan f() untuk masing-masing nilai dalam kisaran sehingga f(x) untuk setiap nilai

yang kedua

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

menjalankan lambda untuk setiap nilai dalam daftar, sehingga menghasilkan semua fungsi tersebut.


7
2018-05-20 18:42



Orang-orang memberikan jawaban yang bagus tetapi lupa menyebutkan bagian terpenting menurut saya: Dalam contoh kedua, X dari daftar pemahaman TIDAK sama dengan X dari lambda berfungsi, mereka sama sekali tidak berhubungan. Jadi contoh kedua sebenarnya sama dengan:

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

Iterasi internal range(10) hanya bertanggung jawab untuk membuat 10 fungsi lambda serupa dalam daftar (10 fungsi terpisah tetapi sangat mirip - mengembalikan daya 2 dari setiap input).

Di sisi lain, contoh pertama bekerja sangat berbeda, karena X dari iterasi DO berinteraksi dengan hasil, untuk setiap iterasi nilainya X*X jadi hasilnya akan seperti itu [0,1,4,9,16,25, 36, 49, 64 ,81]


2
2017-08-27 15:46



Jawaban lainnya benar, tetapi jika Anda mencoba membuat daftar fungsi, masing-masing dengan parameter yang berbeda, yang dapat dieksekusi kemudian, kode berikut akan melakukannya:

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]

Sementara contohnya dibuat-buat, saya merasa ini berguna ketika saya menginginkan daftar fungsi yang masing-masing mencetak sesuatu yang berbeda, yaitu

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

for i in a:
    i()

2
2018-01-24 14:52