Misalkan yang berikut:
>>> s = set([1, 2, 3])
Bagaimana cara mendapatkan nilai (nilai apa pun) s
tanpa melakukannya s.pop()
? Saya ingin meninggalkan item di set sampai saya yakin saya bisa menghapusnya - sesuatu yang saya hanya bisa yakin setelah panggilan asinkron ke host lain.
Cepat dan kotor:
>>> elem = s.pop()
>>> s.add(elem)
Tetapi apakah Anda tahu cara yang lebih baik? Idealnya dalam waktu yang konstan.
union
dll tidak mengambil elemen darinya. Misalnyanext(iter({3,2,1}))
selalu kembali1
jadi jika Anda berpikir bahwa ini akan mengembalikan elemen acak - tidak akan. Jadi mungkin Anda hanya menggunakan struktur data yang salah? Apa gunanya?Jawaban:
Dua opsi yang tidak perlu menyalin seluruh rangkaian:
Atau...
Tetapi secara umum, set tidak mendukung pengindeksan atau pemotongan.
sumber
iter(s).next()
tidak kasar tapi bagus. Sepenuhnya umum untuk mengambil elemen sewenang-wenang dari objek yang dapat diubah. Pilihan Anda jika Anda ingin berhati-hati jika koleksinya kosong.next(iter(your_list or []), None)
untuk menangani Tidak ada set dan set kosongKode paling tidak adalah:
Jelas ini akan membuat daftar baru yang berisi setiap anggota set, jadi tidak bagus jika set Anda sangat besar.
sumber
next(iter(s))
hanya melebihilist(s)[0]
oleh tiga karakter dan sebaliknya secara dramatis unggul dalam waktu dan kompleksitas ruang. Jadi, sementara klaim "kode paling sedikit" sepele benar, itu juga sepele benar bahwa ini adalah pendekatan terburuk yang mungkin. Bahkan menghapus secara manual dan kemudian menambahkan kembali elemen yang dihapus ke set asli lebih unggul daripada "membangun sebuah wadah baru hanya untuk mengekstrak elemen pertama," yang jelas-jelas gila. Yang lebih mengkhawatirkan saya adalah bahwa 38 Stackoverflower benar-benar menaikkan peringkat ini. Saya hanya tahu saya akan melihat ini dalam kode produksi.min(s)
gunakan karakter lebih sedikit sambil menjadi mengerikan dan tidak efisien seperti ini.min(s)
sedikit lebih cepat daripadanext(iter(s))
untuk set ukuran 1, dan saya sampai pada jawaban ini secara khusus mencari kasus khusus yang mengekstraksi satu-satunya elemen dari set ukuran 1.Saya bertanya-tanya bagaimana fungsi akan tampil untuk set yang berbeda, jadi saya melakukan benchmark:
Plot ini jelas menunjukkan bahwa beberapa pendekatan (
RandomSample
,SetUnpacking
danListIndex
) bergantung pada ukuran set dan harus dihindari dalam kasus umum (setidaknya jika kinerja mungkin penting). Seperti yang sudah ditunjukkan oleh jawaban lain, cara tercepat adalahForLoop
.Namun selama salah satu pendekatan waktu konstan digunakan, perbedaan kinerja akan diabaikan.
iteration_utilities
(Penafian: Saya penulis) berisi fungsi kenyamanan untuk kasus penggunaan inifirst
::Saya juga memasukkannya dalam benchmark di atas. Ini dapat bersaing dengan dua solusi "cepat" lainnya tetapi perbedaannya tidak banyak.
sumber
tl; dr
for first_item in muh_set: break
tetap menjadi pendekatan optimal dalam Python 3.x. Terkutuklah kamu, Guido.kamu melakukan ini
Selamat datang di rangkaian Python 3.x lainnya, diekstrapolasi dari wr. Ini sangat baik Python respon 2.x-spesifik . Tidak seperti AChampion yang juga sangat membantu Python 3.x respon spesifik , timing di bawah ini juga time outlier solution yang disarankan di atas - termasuk:
list(s)[0]
, Solusi berbasis urutan novel John .random.sample(s, 1)
, dF. Solusi berbasis RNG eklektik .Cuplikan Kode untuk Kegembiraan Besar
Aktifkan, dengar, atur waktu:
Pengaturan waktu abadi cepat
Melihat! Dipesan oleh snippet tercepat hingga paling lambat:
Faceplants untuk Seluruh Keluarga
Tidak mengherankan, iterasi manual tetap setidaknya dua kali lebih cepat dari solusi tercepat berikutnya. Meskipun kesenjangan telah berkurang dari Bad Old Python 2.x hari (di mana iterasi manual setidaknya empat kali lebih cepat), mengecewakan PEP 20 fanatik pada saya bahwa solusi yang paling bertele-tele adalah yang terbaik. Setidaknya mengubah set ke daftar hanya untuk mengekstrak elemen pertama set sama mengerikan seperti yang diharapkan. Terima kasih Guido, semoga cahayanya terus membimbing kita.
Anehnya, solusi berbasis RNG benar-benar mengerikan. Konversi daftar buruk, tetapi
random
benar - benar memakan kue saus yang enak. Begitu banyak untuk Dewa Angka Acak .Saya hanya berharap yang amorf. Mereka akan menggunakan
set.get_first()
metode untuk kita. Jika Anda membaca ini, Mereka: "Tolong. Lakukan sesuatu."sumber
next(iter(s))
dua kali lebih lambat daripadafor x in s: break
diCPython
agak aneh. Maksud saya ituCPython
. Ini akan menjadi sekitar 50-100 kali (atau sesuatu seperti itu) lebih lambat daripada C atau Haskell melakukan hal yang sama (untuk sebagian besar waktu, terutama dalam iterasi, tidak ada penghapusan panggilan ekor dan tidak ada optimasi sama sekali.). Kehilangan beberapa mikrodetik tidak membuat perbedaan nyata. Bukankah begitu? Dan ada juga PyPyUntuk memberikan beberapa angka waktu di balik pendekatan yang berbeda, pertimbangkan kode berikut. Get () adalah tambahan kustom saya untuk setobject.c Python, menjadi hanya pop () tanpa menghapus elemen.
Outputnya adalah:
Ini berarti bahwa solusi for / break adalah yang tercepat (terkadang lebih cepat daripada solusi custom get ()).
sumber
for x in s
juga? "Sebuah iterator dibuat untuk hasilexpression_list
."s.remove()
ke dalam campuraniter
contoh keduanyafor
daniter
menjadi buruk.Karena Anda ingin elemen acak, ini juga akan berfungsi:
Dokumentasi tampaknya tidak menyebutkan kinerja
random.sample
. Dari tes empiris yang sangat cepat dengan daftar besar dan satu set besar, tampaknya menjadi waktu yang konstan untuk daftar tetapi tidak untuk set. Juga, iterasi pada set tidak acak; pesanan tidak ditentukan tetapi dapat diprediksi:Jika keacakan penting dan Anda membutuhkan banyak elemen dalam waktu yang konstan (kumpulan besar), saya akan menggunakan
random.sample
dan mengonversi ke daftar terlebih dahulu:sumber
choice()
, karena Python akan mencoba mengindeks set Anda dan itu tidak berhasil.Tampaknya yang paling ringkas (6 simbol) meskipun cara yang sangat lambat untuk mendapatkan elemen set (dimungkinkan oleh PEP 3132 ):
Dengan Python 3.5+ Anda juga dapat menggunakan ekspresi 7-simbol ini (terima kasih kepada PEP 448 ):
Kedua opsi kira-kira 1000 kali lebih lambat pada mesin saya daripada metode for-loop.
sumber
Saya menggunakan fungsi utilitas yang saya tulis. Namanya agak menyesatkan karena agak menyiratkan itu mungkin item acak atau sesuatu seperti itu.
sumber
Mengikuti @wr. posting, saya mendapatkan hasil yang serupa (untuk Python3.5)
Keluaran:
Namun, ketika mengubah himpunan yang mendasarinya (mis. Panggilan ke
remove()
) hal-hal buruk terjadi untuk contoh yang dapat diubah (for
,iter
):Hasil dalam:
sumber
Apa yang biasanya saya lakukan untuk koleksi kecil adalah membuat semacam metode parser / converter seperti ini
Kemudian saya dapat menggunakan daftar baru dan akses dengan nomor indeks
Sebagai daftar, Anda akan memiliki semua metode lain yang mungkin perlu Anda kerjakan
sumber
list
alih-alih membuat metode konverter?Bagaimana dengan
s.copy().pop()
? Saya belum menghitung waktunya, tetapi harus berhasil dan sederhana. Ini bekerja paling baik untuk set kecil, karena menyalin seluruh set.sumber
Pilihan lain adalah menggunakan kamus dengan nilai yang tidak Anda pedulikan. Misalnya,
Anda bisa memperlakukan kunci sebagai set kecuali bahwa itu hanya array:
Efek samping dari pilihan ini adalah bahwa kode Anda akan kompatibel dengan
set
Python versi lama. Itu mungkin bukan jawaban terbaik tapi itu pilihan lain.Sunting: Anda bahkan dapat melakukan sesuatu seperti ini untuk menyembunyikan fakta bahwa Anda menggunakan dict alih-alih array atau set:
sumber