Apa cara terbaik, baik secara estetika dan dari perspektif kinerja, untuk membagi daftar item menjadi beberapa daftar berdasarkan persyaratan? Setara dengan:
good = [x for x in mylist if x in goodvals]
bad = [x for x in mylist if x not in goodvals]
apakah ada cara yang lebih elegan untuk melakukan ini?
Pembaruan: inilah use case yang sebenarnya, untuk lebih menjelaskan apa yang saya coba lakukan:
# files looks like: [ ('file1.jpg', 33L, '.jpg'), ('file2.avi', 999L, '.avi'), ... ]
IMAGE_TYPES = ('.jpg','.jpeg','.gif','.bmp','.png')
images = [f for f in files if f[2].lower() in IMAGE_TYPES]
anims = [f for f in files if f[2].lower() not in IMAGE_TYPES]
str.split()
, untuk membagi daftar menjadi koleksi sub-daftar berurutan. Misalnyasplit([1,2,3,4,5,3,6], 3) -> ([1,2],[4,5],[6])
, sebagai lawan untuk membagi elemen daftar berdasarkan kategori.IMAGE_TYPES = set('.jpg','.jpeg','.gif','.bmp','.png')
. n (1) bukannya n (o / 2), dengan praktis tidak ada perbedaan dalam keterbacaan.Jawaban:
Kode itu mudah dibaca, dan sangat jelas!
Sekali lagi, ini baik - baik saja!
Mungkin ada sedikit peningkatan kinerja menggunakan set, tetapi ini adalah perbedaan yang sepele, dan saya menemukan pemahaman daftar jauh lebih mudah untuk dibaca, dan Anda tidak perlu khawatir tentang urutan yang kacau, duplikat dihapus seperti itu.
Bahkan, saya dapat melangkah ke belakang "mundur", dan cukup gunakan sederhana untuk loop:
Daftar-pemahaman atau penggunaan
set()
baik-baik saja sampai Anda perlu menambahkan beberapa cek lain atau sedikit logika - katakan Anda ingin menghapus semua jpeg 0-byte, Anda hanya menambahkan sesuatu seperti ..sumber
[x for x in blah if ...]
- verbose,lambda
kikuk dan terbatas ... Rasanya seperti mengendarai mobil paling keren dari 1995 hari ini. Tidak sama dengan saat itu.sumber
good.append(x) if x in goodvals else bad.append(x)
lebih mudah dibaca.x
, Anda dapat menjadikannyaappend
hanya satu :for x in mylist: (good if isgood(x) else bad).append(x)
(bad.append, good.append)
(good if x in goodvals else bad).append(x)
Inilah pendekatan iterator yang malas:
Ini mengevaluasi kondisi sekali per item dan mengembalikan dua generator, pertama menghasilkan nilai dari urutan di mana kondisi itu benar, yang lain di mana itu salah.
Karena malas, Anda dapat menggunakannya di sembarang iterator, bahkan yang tak terbatas:
Biasanya meskipun pendekatan pengembalian daftar yang tidak malas lebih baik:
Sunting: Untuk usecase Anda yang lebih spesifik dari membagi item ke dalam daftar yang berbeda dengan beberapa kunci, inilah fungsi generik yang melakukan itu:
Pemakaian:
sumber
[ x for x in my_list if ExpensiveOperation(x) ]
perlu waktu lama untuk dijalankan, Anda tentu tidak ingin melakukannya dua kali!tee
menyimpan semua nilai di antara iterator yang dikembalikan, sehingga tidak akan benar-benar menghemat memori jika Anda mengulangi seluruh generator dan yang lainnya.Masalah dengan semua solusi yang diajukan adalah akan memindai dan menerapkan fungsi penyaringan dua kali. Saya akan membuat fungsi kecil sederhana seperti ini:
Dengan begitu Anda tidak memproses apa pun dua kali dan juga tidak mengulang kode.
sumber
Saya ambil itu. Saya mengusulkan fungsi lazy, single-pass
partition
, yang menjaga urutan relatif pada output berikutnya.1. Persyaratan
Saya berasumsi bahwa persyaratannya adalah:
i
)filter
ataugroupby
)2.
split
perpustakaanpartition
Fungsi saya (diperkenalkan di bawah) dan fungsi serupa lainnya telah membuatnya menjadi perpustakaan kecil:Ini dapat diinstal secara normal melalui PyPI:
Untuk membagi basis daftar pada kondisi, gunakan
partition
fungsi:3.
partition
fungsi dijelaskanSecara internal kita perlu membangun dua urutan sekaligus, jadi hanya memakan satu urutan output akan memaksa yang lain untuk dihitung juga. Dan kita perlu menjaga keadaan antara permintaan pengguna (toko diproses tetapi elemen belum diminta). Untuk menjaga status, saya menggunakan dua antrian ujung ganda (
deques
):SplitSeq
kelas mengurus rumah tangga:Sihir terjadi dalam
.getNext()
metodenya. Ini hampir seperti.next()
iterator, tetapi memungkinkan untuk menentukan jenis elemen yang kita inginkan kali ini. Di belakang adegan itu tidak membuang elemen yang ditolak, tetapi sebaliknya menempatkan mereka di salah satu dari dua antrian:Pengguna akhir seharusnya menggunakan
partition
fungsi. Dibutuhkan fungsi kondisi dan urutan (sepertimap
ataufilter
), dan mengembalikan dua generator. Generator pertama membangun urutan elemen yang kondisinya berlaku, generator kedua membangun urutan tambahan. Iterator dan generator memungkinkan pemisahan dengan urutan yang panjang atau tidak terbatas.Saya memilih fungsi tes untuk menjadi argumen pertama untuk memfasilitasi aplikasi parsial di masa depan (mirip dengan bagaimana
map
danfilter
memiliki fungsi tes sebagai argumen pertama).sumber
Saya pada dasarnya menyukai pendekatan Anders karena sangat umum. Berikut adalah versi yang mengutamakan kategorizer (untuk mencocokkan sintaks filter) dan menggunakan defaultdict (diasumsikan diimpor).
sumber
Go pertama (pra-OP-edit): Gunakan set:
Itu bagus untuk keterbacaan (IMHO) dan kinerja.
Go kedua (post-OP-edit):
Buat daftar ekstensi yang baik sebagai satu set:
dan itu akan meningkatkan kinerja. Kalau tidak, apa yang Anda miliki terlihat baik bagi saya.
sumber
goodvals
.itertools.groupby hampir melakukan apa yang Anda inginkan, kecuali itu membutuhkan item yang akan disortir untuk memastikan bahwa Anda mendapatkan satu rentang yang berdekatan, jadi Anda perlu mengurutkan berdasarkan kunci Anda terlebih dahulu (jika tidak, Anda akan mendapatkan beberapa grup yang saling terkait untuk setiap jenis). misalnya.
memberi:
Mirip dengan solusi lain, fungsi kunci dapat didefinisikan untuk dibagi menjadi sejumlah grup yang Anda inginkan.
sumber
Jawaban elegan dan ringkas oleh @dansalmo ini muncul di komentar, jadi saya hanya mengepos ulang di sini sebagai jawaban sehingga bisa mendapatkan keunggulan yang layak, terutama bagi pembaca baru.
Contoh lengkap:
sumber
Jika Anda ingin membuatnya dalam gaya FP:
Bukan solusi yang paling mudah dibaca, tetapi paling tidak hanya melalui mylist sekali saja.
sumber
Secara pribadi, saya suka versi yang Anda kutip, dengan asumsi Anda sudah memiliki daftar
goodvals
nongkrong. Jika tidak, sesuatu seperti:Tentu saja, itu sangat mirip dengan menggunakan pemahaman daftar seperti yang Anda lakukan semula, tetapi dengan fungsi alih-alih pencarian:
Secara umum, saya menemukan estetika pemahaman daftar menjadi sangat menyenangkan. Tentu saja, jika Anda tidak benar-benar perlu mempertahankan pemesanan dan tidak perlu duplikat, menggunakan
intersection
dandifference
metode pada set akan bekerja dengan baik juga.sumber
filter(lambda x: is_good(x), mylist)
dapat direduksi menjadifilter(is_good, mylist)
Saya pikir generalisasi dari pemisahan iterable berdasarkan kondisi N berguna
Misalnya:
Jika elemen dapat memenuhi beberapa kondisi, hapus jeda.
sumber
Lihat ini
sumber
Terkadang, sepertinya pemahaman daftar bukan hal terbaik untuk digunakan!
Saya membuat tes kecil berdasarkan jawaban yang diberikan orang pada topik ini, diuji pada daftar yang dibuat secara acak. Inilah generasi daftar (mungkin ada cara yang lebih baik untuk dilakukan, tetapi bukan itu intinya):
Dan di sini kita mulai
Menggunakan fungsi cmpthese , hasil terbaik adalah jawaban dbr:
sumber
Namun solusi lain untuk masalah ini. Saya membutuhkan solusi yang secepat mungkin. Itu berarti hanya satu iterasi di atas daftar dan lebih disukai O (1) untuk menambahkan data ke salah satu daftar yang dihasilkan. Ini sangat mirip dengan solusi yang disediakan oleh sastanin , kecuali jauh lebih pendek:
Kemudian, Anda dapat menggunakan fungsi dengan cara berikut:
Jika Anda tidak baik dengan yang dihasilkan
deque
objek, Anda dapat dengan mudah dikonversi kelist
,set
, apa pun yang Anda seperti (misalnyalist(lower)
). Konversi jauh lebih cepat, yaitu pembangunan daftar secara langsung.Metode ini menjaga urutan barang, serta duplikat.
sumber
Misalnya, daftar pemisahan dengan genap dan ganjil
Atau secara umum:
Keuntungan:
Kekurangan
sumber
Terinspirasi oleh jawaban hebat @ gnibbler (tetapi singkat!) , Kita dapat menerapkan pendekatan itu untuk memetakan ke beberapa partisi:
Kemudian
splitter
bisa digunakan sebagai berikut:Ini berfungsi untuk lebih dari dua partisi dengan pemetaan yang lebih rumit (dan pada iterator juga):
Atau menggunakan kamus untuk memetakan:
sumber
tambahkan pengembalian Tidak ada, jadi itu berfungsi.
sumber
Untuk kinerja, cobalah
itertools
.Lihat itertools.ifilter atau imap.
sumber
[x for x in a if x > 50000]
pada array sederhana 100000 bilangan bulat (melalui random.shuffle),filter(lambda x: x> 50000, a)
akan memakan waktu 2x lebih lama,ifilter(lambda x: x> 50000, a); list(result)
membutuhkan waktu sekitar 2.3x selama. Aneh tapi Nyata.Terkadang Anda tidak membutuhkan separuh daftar lainnya. Sebagai contoh:
sumber
Ini cara tercepat.
Ini menggunakan
if else
, (seperti jawaban dbr) tetapi membuat satu set pertama. Satu set mengurangi jumlah operasi dari O (m * n) ke O (log m) + O (n), menghasilkan 45% + peningkatan kecepatan.Sedikit lebih pendek:
Hasil patok banding:
Kode benchmark lengkap untuk Python 3.7 (dimodifikasi dari FunkySayu):
sumber
Jika Anda bersikeras pandai, Anda bisa mengambil solusi Winden dan sedikit kepintaran palsu:
sumber
Sudah beberapa solusi di sini, tetapi cara lain untuk melakukan itu adalah -
Iterasi daftar hanya sekali, dan terlihat sedikit lebih pythonic dan karenanya dapat dibaca oleh saya.
sumber
Saya akan mengambil pendekatan 2-pass, memisahkan evaluasi predikat dari memfilter daftar:
Apa yang baik tentang ini, kinerja-bijaksana (selain mengevaluasi
pred
hanya sekali pada setiap anggotaiterable
), adalah bahwa ia banyak memindahkan logika dari penerjemah dan ke dalam iterasi yang sangat dioptimalkan dan kode pemetaan. Ini dapat mempercepat iterasi dari iterables yang panjang, seperti dijelaskan dalam jawaban ini .Dari sisi ekspresif, ia memanfaatkan idiom ekspresif seperti pemahaman dan pemetaan.
sumber
larutan
uji
sumber
Jika Anda tidak keberatan menggunakan perpustakaan eksternal di sana dua saya tahu bahwa secara nativ mengimplementasikan operasi ini:
iteration_utilities.partition
:more_itertools.partition
sumber
Tidak yakin apakah ini pendekatan yang baik tetapi bisa dilakukan dengan cara ini juga
sumber
Jika daftar terbuat dari grup dan pemisah berselang, Anda dapat menggunakan:
Pemakaian:
sumber
Bagus ketika kondisinya lebih lama, seperti pada contoh Anda. Pembaca tidak harus mencari tahu kondisi negatif dan apakah ia menangkap semua kasus lainnya.
sumber
Namun jawaban lain, pendek tapi "jahat" (untuk efek samping daftar-pemahaman).
sumber