Saya kebetulan menemukan diri saya memiliki kebutuhan penyaringan dasar: Saya punya daftar dan saya harus menyaringnya dengan atribut item.
Kode saya terlihat seperti ini:
my_list = [x for x in my_list if x.attribute == value]
Tapi kemudian saya berpikir, bukankah lebih baik menulis seperti ini?
my_list = filter(lambda x: x.attribute == value, my_list)
Ini lebih mudah dibaca, dan jika diperlukan untuk kinerja lambda dapat diambil untuk mendapatkan sesuatu.
Pertanyaannya adalah: apakah ada peringatan dalam menggunakan cara kedua? Adakah perbedaan kinerja? Apakah saya kehilangan Pythonic Way ™ sepenuhnya dan harus melakukannya dengan cara lain (seperti menggunakan itemgetter bukan lambda)?
filter
lebih mudah dibaca. Ketika Anda memiliki ekspresi sederhana yang dapat digunakan apa adanya di listcomp, tetapi harus dibungkus dengan lambda (atau dibangun di luar daripartial
atauoperator
fungsi, dll) untuk dilewatifilter
, saat itulah Listcomps menang.filter
adalah objek generator filter bukan daftar.Jawaban:
Sungguh aneh betapa banyak keindahan bervariasi untuk orang yang berbeda. Saya menemukan pemahaman daftar jauh lebih jelas daripada
filter
+lambda
, tetapi gunakan mana yang Anda temukan lebih mudah.Ada dua hal yang dapat memperlambat penggunaan Anda
filter
.Yang pertama adalah overhead panggilan fungsi: segera setelah Anda menggunakan fungsi Python (apakah dibuat oleh
def
ataulambda
), kemungkinan filter akan lebih lambat daripada pemahaman daftar. Ini hampir pasti tidak cukup untuk masalah, dan Anda tidak harus berpikir banyak tentang kinerja sampai Anda menghitung waktu kode Anda dan menemukan itu menjadi hambatan, tetapi perbedaannya ada di sana.Overhead lain yang mungkin berlaku adalah bahwa lambda dipaksa untuk mengakses variabel scoped (
value
). Itu lebih lambat daripada mengakses variabel lokal dan dalam Python 2.x daftar pemahaman hanya mengakses variabel lokal. Jika Anda menggunakan Python 3.x pemahaman daftar berjalan dalam fungsi terpisah sehingga juga akan mengaksesvalue
melalui penutupan dan perbedaan ini tidak akan berlaku.Pilihan lain untuk dipertimbangkan adalah menggunakan generator alih-alih pemahaman daftar:
Kemudian dalam kode utama Anda (yang mana keterbacaan sangat penting) Anda telah mengganti pemahaman daftar dan filter dengan nama fungsi yang diharapkan bermakna.
sumber
[]
ke()
. Juga, saya setuju bahwa daftar comp lebih indah.filter
lebih cepat menggunakan fungsi panggilan balik Python.Ini adalah masalah yang agak religius dalam Python. Meskipun Guido mempertimbangkan untuk menghapus
map
,filter
danreduce
dari Python 3 , ada cukup serangan balik yang pada akhirnya hanyareduce
dipindahkan dari built-in ke functools.reduce .Secara pribadi saya menemukan daftar pemahaman lebih mudah dibaca. Lebih eksplisit apa yang terjadi dari ekspresi
[i for i in list if i.attribute == value]
karena semua perilaku di permukaan bukan di dalam fungsi filter.Saya tidak akan terlalu khawatir tentang perbedaan kinerja antara kedua pendekatan karena marjinal. Saya benar-benar hanya akan mengoptimalkan ini jika terbukti menjadi hambatan dalam aplikasi Anda yang tidak mungkin.
Juga karena BDFL ingin
filter
pergi dari bahasa maka pasti itu secara otomatis membuat daftar pemahaman lebih Pythonic ;-)sumber
Karena perbedaan kecepatan apa pun pasti sangat kecil, apakah menggunakan filter atau daftar pemahaman menjadi masalah selera. Secara umum saya cenderung menggunakan pemahaman (yang tampaknya setuju dengan sebagian besar jawaban lain di sini), tetapi ada satu kasus di mana saya lebih suka
filter
.Kasus penggunaan yang sangat sering adalah menarik keluar nilai dari beberapa subjek X yang dapat diubah ke predikat P (x):
tetapi terkadang Anda ingin menerapkan beberapa fungsi ke nilai terlebih dahulu:
Sebagai contoh spesifik, pertimbangkan
Saya pikir ini terlihat sedikit lebih baik daripada menggunakan
filter
. Tapi sekarang pertimbangkanDalam hal ini kami ingin
filter
menentang nilai pasca-perhitungan. Selain masalah menghitung kubus dua kali (bayangkan perhitungan yang lebih mahal), ada masalah menulis ekspresi dua kali, melanggar estetika KERING . Dalam hal ini saya akan cenderung menggunakansumber
[prime(i) for i in [x**3 for x in range(1000)]]
x*x*x
tidak bisa menjadi bilangan prima, seperti yang terjadix^2
danx
sebagai faktor, contohnya tidak benar-benar masuk akal secara matematis, tapi mungkin itu masih membantu. (Mungkin kita bisa menemukan sesuatu yang lebih baik?)prime_cubes = filter(prime, (x*x*x for x in range(1000)))
prime_cubes = [1]
menghemat memori dan siklus cpu ;-)[]
Meskipun
filter
mungkin merupakan "jalan tercepat", "jalan Pythonic" tidak akan peduli tentang hal-hal seperti itu kecuali kinerja sangat penting (dalam hal ini Anda tidak akan menggunakan Python!).sumber
Saya pikir saya hanya akan menambahkan bahwa di python 3, filter () sebenarnya adalah objek iterator, jadi Anda harus melewati metode panggilan Anda ke daftar () untuk membangun daftar yang difilter. Jadi dengan python 2:
daftar b dan c memiliki nilai yang sama, dan diselesaikan dalam waktu yang hampir bersamaan dengan filter () sama dengan [x untuk x dalam y jika z]. Namun, dalam 3, kode yang sama ini akan meninggalkan daftar c yang berisi objek filter, bukan daftar yang difilter. Untuk menghasilkan nilai yang sama dalam 3:
Masalahnya adalah list () mengambil iterable sebagai argumennya, dan membuat daftar baru dari argumen itu. Hasilnya adalah bahwa menggunakan filter dengan cara ini di python 3 membutuhkan waktu hingga dua kali lebih lama daripada metode [x untuk x in y jika z] karena Anda harus beralih pada output dari filter () serta daftar asli.
sumber
Perbedaan penting adalah bahwa pemahaman daftar akan mengembalikan beberapa
list
saat filter mengembalikan afilter
, yang Anda tidak dapat memanipulasi seperti alist
(yaitu: panggillen
, yang tidak bekerja dengan pengembalianfilter
).Belajar mandiri saya sendiri membawa saya ke beberapa masalah serupa.
Yang sedang berkata, jika ada cara untuk memiliki hasil
list
darifilter
, sedikit seperti yang Anda lakukan di. NET ketika Anda melakukannyalst.Where(i => i.something()).ToList()
, saya ingin tahu.EDIT: Ini adalah kasus untuk Python 3, bukan 2 (lihat diskusi dalam komentar).
sumber
a = [1, 2, 3, 4, 5, 6, 7, 8]
f = filter(lambda x: x % 2 == 0, a)
lc = [i for i in a if i % 2 == 0]
>>> type(f)
<class 'filter'>
>>> type(lc)
<class 'list'>
list()
pada hasil:list(filter(my_func, my_iterable))
. Dan tentu saja Anda bisa menggantinyalist
denganset
, atautuple
, atau apa pun yang membutuhkan perubahan. Tetapi bagi siapa pun selain programmer fungsional, kasus ini bahkan lebih kuat untuk menggunakan pemahaman daftar daripadafilter
ditambah konversi eksplisit kelist
.Saya menemukan cara kedua lebih mudah dibaca. Ini memberitahu Anda apa maksudnya: filter daftar.
PS: jangan gunakan 'daftar' sebagai nama variabel
sumber
umumnya
filter
sedikit lebih cepat jika menggunakan fungsi builtin.Saya berharap pemahaman daftar menjadi sedikit lebih cepat dalam kasus Anda
sumber
Filter hanya itu. Ini menyaring elemen daftar. Anda dapat melihat definisi menyebutkan hal yang sama (di tautan dokumen resmi yang saya sebutkan sebelumnya). Sedangkan, pemahaman daftar adalah sesuatu yang menghasilkan daftar baru setelah bertindak atas sesuatu pada daftar sebelumnya. (Kedua filter dan pemahaman daftar membuat daftar baru dan tidak melakukan operasi di tempat daftar yang lebih lama. Daftar baru di sini adalah sesuatu seperti daftar dengan , katakanlah, tipe data yang sama sekali baru. Seperti mengonversi bilangan bulat menjadi string, dll)
Dalam contoh Anda, lebih baik menggunakan filter daripada pemahaman daftar, sesuai definisi. Namun, jika Anda ingin, katakan other_attribute dari elemen daftar, dalam contoh Anda akan diambil sebagai daftar baru, maka Anda dapat menggunakan pemahaman daftar.
Ini adalah bagaimana saya benar-benar ingat tentang pemahaman filter dan daftar. Hapus beberapa hal dalam daftar dan jaga elemen lainnya tetap utuh, gunakan filter. Gunakan beberapa logika Anda sendiri di elemen dan buat daftar encer yang cocok untuk beberapa tujuan, gunakan pemahaman daftar.
sumber
Berikut adalah bagian pendek yang saya gunakan ketika saya perlu memfilter sesuatu setelah pemahaman daftar. Hanya kombinasi filter, lambda, dan daftar (atau dikenal sebagai kesetiaan kucing dan kebersihan anjing).
Dalam hal ini saya membaca file, menghapus garis kosong, mengomentari baris, dan apa pun setelah komentar pada baris:
sumber
file_contents = list(filter(None, (s.partition('#')[0].strip() for s in lines)))
Selain jawaban yang diterima, ada sudut kasus saat Anda harus menggunakan filter alih-alih pemahaman daftar. Jika daftar itu tidak dapat dilanggar, Anda tidak dapat langsung memprosesnya dengan pemahaman daftar. Contoh dunia nyata adalah jika Anda menggunakan
pyodbc
untuk membaca hasil dari database. ThefetchAll()
Hasil daricursor
adalah daftar unhashable. Dalam situasi ini, untuk secara langsung memanipulasi hasil yang dikembalikan, filter harus digunakan:Jika Anda menggunakan pemahaman daftar di sini Anda akan mendapatkan kesalahan:
sumber
>>> hash(list()) # TypeError: unhashable type: 'list'
kedua ini berfungsi dengan baik:processed_data = [s for s in data_from_db if 'abc' in s.field1 or s.StartTime >= start_date_time]
Butuh beberapa waktu untuk membiasakan diri dengan
higher order functions
filter
danmap
. Jadi saya terbiasa dengan mereka dan saya benar-benar sukafilter
karena secara eksplisit itu menyaring dengan menjaga apa pun yang benar dan saya merasa keren bahwa saya tahu beberapafunctional programming
istilah.Lalu saya membaca bagian ini (Fluent Python Book):
Dan sekarang saya pikir, mengapa repot-repot dengan konsep
filter
/map
jika Anda dapat mencapainya dengan idiom yang sudah menyebar luas seperti daftar pemahaman. Lebih jauh lagimaps
danfilters
semacam fungsi. Dalam hal ini saya lebih suka menggunakanAnonymous functions
lambdas.Akhirnya, hanya untuk menguji, saya menghitung waktu kedua metode (
map
danlistComp
) dan saya tidak melihat perbedaan kecepatan yang relevan yang akan membenarkan membuat argumen tentang hal itu.sumber
Anehnya pada Python 3, saya melihat filter berkinerja lebih cepat daripada daftar pemahaman.
Saya selalu berpikir bahwa pemahaman daftar akan lebih baik. Sesuatu seperti: [nama untuk nama di brand_names_db jika nama tidak ada] Bytecode yang dihasilkan sedikit lebih baik.
Tapi mereka sebenarnya lebih lambat:
sumber
if not None
dalam daftar pemahaman Anda sedang mendefinisikan fungsi lambda (perhatikanMAKE_FUNCTION
pernyataan). Kedua, hasilnya berbeda, karena versi daftar pemahaman akan menghapus hanyaNone
nilai, sedangkan versi filter akan menghapus semua nilai "falsy". Karena itu, seluruh tujuan microbenchmarking tidak berguna. Itu adalah satu juta iterasi, kali item 1k! Perbedaannya dapat diabaikan .Saya ambil
sumber
i
tidak pernah dikatakan sebagaidict
, dan tidak perlulimit
. Selain itu, bagaimana ini berbeda dari apa yang disarankan OP, dan bagaimana hal itu menjawab pertanyaan?