Saya memiliki daftar objek dan saya ingin menghapus semua objek yang kosong kecuali satu, menggunakan filter
dan lambda
ekspresi.
Misalnya jika inputnya adalah:
[Object(name=""), Object(name="fake_name"), Object(name="")]
... maka outputnya harus:
[Object(name=""), Object(name="fake_name")]
Apakah ada cara untuk menambahkan tugas ke lambda
ekspresi? Sebagai contoh:
flag = True
input = [Object(name=""), Object(name="fake_name"), Object(name="")]
output = filter(
(lambda o: [flag or bool(o.name), flag = flag and bool(o.name)][0]),
input
)
Jawaban:
Operator ekspresi penugasan yang
:=
ditambahkan di Python 3.8 mendukung penugasan di dalam ekspresi lambda. Operator ini hanya dapat muncul dalam ekspresi dalam tanda kurung(...)
, tanda kurung[...]
, atau tanda kurung{...}
untuk alasan sintaksis. Misalnya, kita bisa menulis yang berikut ini:Dalam Python 2, dimungkinkan untuk melakukan tugas lokal sebagai efek samping dari pemahaman daftar.
Namun, tidak mungkin untuk menggunakan salah satu dari ini dalam contoh Anda karena variabel Anda
flag
berada di lingkup luar, bukanlambda
cakupannya. Ini tidak ada hubungannya denganlambda
, ini adalah perilaku umum dalam Python 2. Python 3 memungkinkan Anda menyiasati ini dengannonlocal
kata kunci di dalamdef
s, tetapinonlocal
tidak dapat digunakan di dalamlambda
s.Ada solusinya (lihat di bawah), tetapi saat kita membahas topik ...
Dalam beberapa kasus, Anda dapat menggunakan ini untuk melakukan semua yang ada di dalam
lambda
:Tolong jangan.
... kembali ke contoh awal Anda: meskipun Anda tidak dapat melakukan tugas ke
flag
variabel di lingkup luar, Anda dapat menggunakan fungsi untuk mengubah nilai yang ditetapkan sebelumnya.Misalnya,
flag
bisa menjadi objek yang.value
kita atur menggunakansetattr
:Jika kita ingin menyesuaikan tema di atas, kita bisa menggunakan pemahaman daftar alih-alih
setattr
:Tapi sungguh, dalam kode serius Anda harus selalu menggunakan definisi fungsi biasa daripada
lambda
jika Anda akan melakukan tugas luar.sumber
.setattr()
and alikes ( kamus harus melakukannya juga, misalnya) untuk meretas efek samping menjadi kode fungsional, kode keren oleh @JeremyBanks ditampilkan :)assignment operator
!Anda tidak dapat benar-benar mempertahankan status dalam
filter
/lambda
ekspresi (kecuali menyalahgunakan namespace global). Namun Anda dapat mencapai sesuatu yang serupa menggunakan hasil akumulasi yang diteruskan dalamreduce()
ekspresi:Anda dapat, tentu saja, sedikit mengubah kondisi. Dalam hal ini, ini menyaring duplikat, tetapi Anda juga dapat menggunakan
a.count("")
, misalnya, untuk hanya membatasi string kosong.Tak perlu dikatakan, Anda bisa melakukan ini tetapi sebenarnya tidak boleh. :)
Terakhir, Anda dapat melakukan apa saja dengan Python murni
lambda
: http://vanderwijk.info/blog/pure-lambda-calculus-python/sumber
Tidak perlu menggunakan lambda, saat Anda dapat menghapus semua yang null, dan mengembalikannya jika ukuran input berubah:
sumber
output = [x for x in input if x.name]
.Penugasan normal (
=
) tidak dimungkinkan di dalamlambda
ekspresi, meskipun dimungkinkan untuk melakukan berbagai trik dengansetattr
dan teman.Namun, menyelesaikan masalah Anda sebenarnya cukup sederhana:
yang akan memberimu
Seperti yang Anda lihat, ini menyimpan contoh kosong pertama, bukan yang terakhir. Jika Anda membutuhkan yang terakhir, balikkan daftar menjadi
filter
, dan balikkan daftar yang keluar darifilter
:yang akan memberimu
Satu hal yang harus diperhatikan: agar ini berfungsi dengan objek arbitrer, objek tersebut harus diimplementasikan dengan benar
__eq__
dan__hash__
seperti yang dijelaskan di sini .sumber
PERBARUI :
atau menggunakan
filter
danlambda
:Jawaban Sebelumnya
Oke, apakah Anda terjebak menggunakan filter dan lambda?
Sepertinya ini akan lebih baik disajikan dengan pemahaman kamus,
Saya pikir alasan mengapa Python tidak mengizinkan penugasan dalam lambda mirip dengan mengapa ia tidak mengizinkan penugasan dalam pemahaman dan itu ada hubungannya dengan fakta bahwa hal-hal ini dievaluasi di
C
samping dan dengan demikian dapat memberi kita sebuah peningkatan kecepatan. Setidaknya itulah kesan saya setelah membaca salah satu esai Guido .Dugaan saya ini juga akan bertentangan dengan filosofi memiliki satu cara yang benar dalam melakukan satu hal dengan Python.
sumber
TL; DR: Saat menggunakan idiom fungsional, lebih baik menulis kode fungsional
Seperti yang telah ditunjukkan banyak orang, dalam Python penugasan lambdas tidak diperbolehkan. Secara umum bila menggunakan idiom fungsional lebih baik Anda berpikir secara fungsional yang berarti sedapat mungkin tidak ada efek samping dan tidak ada tugas.
Berikut adalah solusi fungsional yang menggunakan lambda. Saya telah menetapkan lambda ke
fn
untuk kejelasan (dan karena itu sedikit panjang).Anda juga dapat membuat kesepakatan ini dengan iterator daripada daftar dengan mengubah sedikit hal. Anda juga memiliki beberapa impor berbeda.
Anda selalu dapat mengubah kode untuk mengurangi panjang pernyataan.
sumber
Jika alih-alih
flag = True
kita dapat melakukan impor, maka menurut saya ini memenuhi kriteria:Atau mungkin filter lebih baik ditulis sebagai:
Atau, hanya untuk boolean sederhana, tanpa impor apa pun:
sumber
Cara pythonic untuk melacak status selama iterasi adalah dengan generator. Cara itertools cukup sulit untuk dipahami IMHO dan mencoba meretas lambda untuk melakukan ini sangat konyol. Saya akan mencoba:
Secara keseluruhan, keterbacaan mengalahkan kekompakan setiap saat.
sumber
Tidak, Anda tidak dapat meletakkan tugas di dalam lambda karena definisinya sendiri. Jika Anda bekerja menggunakan pemrograman fungsional, maka Anda harus berasumsi bahwa nilai-nilai Anda tidak dapat berubah.
Salah satu solusinya adalah kode berikut:
sumber
Jika Anda memerlukan lambda untuk mengingat status antar panggilan, saya akan merekomendasikan fungsi yang dideklarasikan di namespace lokal atau kelas dengan kelebihan beban
__call__
. Sekarang semua peringatan saya terhadap apa yang Anda coba lakukan sudah keluar dari jalan, kita bisa mendapatkan jawaban yang sebenarnya untuk pertanyaan Anda.Jika Anda benar-benar membutuhkan lambda Anda untuk memiliki beberapa memori di antara panggilan, Anda dapat menentukannya seperti:
Maka Anda hanya perlu meneruskan
f
kefilter()
. Jika Anda benar-benar perlu, Anda bisa mendapatkan kembali nilaiflag
berikut ini:Alternatifnya, Anda dapat mengubah namespace global dengan mengubah hasil
globals()
. Sayangnya, Anda tidak dapat mengubah namespace lokal dengan cara yang sama seperti mengubah hasil darilocals()
tidak memengaruhi namespace lokal.sumber
(let ((var 42)) (lambda () (setf var 43)))
.Anda dapat menggunakan fungsi bind untuk menggunakan lambda pseudo multi-pernyataan. Kemudian Anda dapat menggunakan kelas pembungkus untuk Bendera untuk mengaktifkan tugas.
sumber
Jenis solusi yang berantakan, tetapi penugasan di lambda adalah ilegal, jadi itu tidak terlalu penting. Anda dapat menggunakan
exec()
fungsi builtin untuk menjalankan tugas dari dalam lambda, seperti contoh berikut:sumber
pertama, Anda tidak perlu menggunakan tugas lokal untuk pekerjaan Anda, cukup periksa jawaban di atas
kedua, mudah untuk menggunakan local () dan global () untuk mendapatkan tabel variabel dan kemudian mengubah nilainya
periksa kode contoh ini:
jika Anda perlu mengubah menambahkan variabel global ke lingkungan Anda, coba ganti lokal () dengan global ()
comp daftar python keren tetapi sebagian besar proyek tridisional tidak menerima ini (seperti flask: [)
semoga bisa membantu
sumber
locals()
, secara eksplisit dikatakan dalam dokumentasi bahwa mengubahnya tidak benar-benar mengubah cakupan lokal (atau setidaknya tidak selalu).globals()
di sisi lain bekerja seperti yang diharapkan.globals()
. pastebin.com/5Bjz1mR4 (diuji di 2.6 dan 3.2) membuktikannya.