Saya memiliki skrip Python sebagai input daftar bilangan bulat, yang saya perlukan untuk bekerja dengan empat bilangan bulat sekaligus. Sayangnya, saya tidak memiliki kendali atas input, atau saya akan meneruskannya sebagai daftar tupel empat elemen. Saat ini, saya mengulanginya dengan cara ini:
for i in xrange(0, len(ints), 4):
# dummy op for example code
foo += ints[i] * ints[i + 1] + ints[i + 2] * ints[i + 3]
Namun, sepertinya "C-think", yang membuat saya curiga ada cara yang lebih pythonic dalam menangani situasi ini. Daftar ini dibuang setelah iterasi, jadi tidak perlu dilestarikan. Mungkin sesuatu seperti ini akan lebih baik?
while ints:
foo += ints[0] * ints[1] + ints[2] * ints[3]
ints[0:4] = []
Meskipun demikian, masih tidak terlalu "merasa" benar. : - /
Pertanyaan terkait: Bagaimana Anda membagi daftar menjadi potongan berukuran rata dengan Python?
Jawaban:
Dimodifikasi dari bagian resep dokumen itertools Python :
Contoh
Dalam pseudocode untuk menjaga contoh tetap.
Catatan: pada Python 2 gunakan
izip_longest
sebagai gantizip_longest
.sumber
izip_longest
akan diberi argumen 256 ribu.None
mengisi potongan terakhir?Sederhana. Mudah. Cepat. Bekerja dengan urutan apa pun:
sumber
itertools
modul.chunker
mengembalikan agenerator
. Ganti kembali ke:return [...]
untuk mendapatkan daftar.yield
:for pos in xrange(0, len(seq), size): yield seq[pos:pos + size]
. Saya tidak yakin apakah secara internal ini akan ditangani secara berbeda dalam aspek yang relevan, tetapi mungkin bahkan sedikit lebih jelas.__getitem__
metode.Saya penggemar
sumber
chunk
akan memiliki 1, 2 atau 3 elemen untuk kumpulan elemen terakhir. Lihat pertanyaan ini tentang mengapa indeks slice bisa di luar batas .Cara lain:
sumber
size
, yang kadang-kadang diinginkan.len
panggilan dan karenanya tidak berfungsi pada generator lain.sumber
izip_longest
diganti olehzip_longest
Solusi ideal untuk masalah ini bekerja dengan iterator (bukan hanya urutan). Itu juga harus cepat.
Ini adalah solusi yang disediakan oleh dokumentasi untuk itertools:
Menggunakan ipython
%timeit
di udara buku mac saya, saya mendapatkan 47,5 kita per loop.Namun, ini benar-benar tidak berhasil bagi saya karena hasilnya empuk untuk menjadi kelompok berukuran genap. Solusi tanpa bantalan sedikit lebih rumit. Solusi yang paling naif mungkin:
Sederhana, tetapi sangat lambat: 693 us per loop
Solusi terbaik yang bisa saya gunakan
islice
untuk loop dalam:Dengan dataset yang sama, saya mendapatkan 305 us per loop.
Tidak dapat memperoleh solusi murni lebih cepat dari itu, saya memberikan solusi berikut dengan peringatan penting: Jika data input Anda memiliki instance
filldata
di dalamnya, Anda bisa mendapatkan jawaban yang salah.Saya benar-benar tidak suka jawaban ini, tetapi secara signifikan lebih cepat. 124 us per loop
sumber
itertools
impor;map
harus Py3map
atauimap
):def grouper(n, it): return takewhile(bool, map(tuple, starmap(islice, repeat((iter(it), n)))))
. Fungsi akhir Anda dapat dibuat kurang rapuh dengan menggunakan sentinel: singkirkanfillvalue
argumen; tambahkan baris pertamafillvalue = object()
, lalu ubah tandaif
centang keif i[-1] is fillvalue:
dan baris yang dikontrolnyayield tuple(v for v in i if v is not fillvalue)
. Jaminan tidak ada nilai dalamiterable
dapat keliru dengan nilai pengisi.islice
objek (# 3 menang jikan
relatif besar, misalnya jumlah kelompok kecil, tapi itu mengoptimalkan untuk kasus yang tidak biasa), tapi saya tidak berharap itu cukup seperti itu ekstrim.izip_longest
pada tuple akhir:yield i[:modulo]
. Juga, untukargs
variabel, tuple itu bukan daftar:args = (iter(iterable),) * n
. Memotong beberapa siklus lagi. Terakhir, jika kita mengabaikan nilai fill dan menganggapNone
, kondisi bisa menjadiif None in i
siklus clock bahkan lebih.yield
), sedangkan kasing umum tidak terpengaruh.Saya membutuhkan solusi yang juga berfungsi dengan set dan generator. Saya tidak dapat menemukan sesuatu yang sangat pendek dan cantik, tetapi setidaknya cukup mudah dibaca.
Daftar:
Set:
Generator:
sumber
Mirip dengan proposal lain, tetapi tidak persis sama, saya suka melakukannya dengan cara ini, karena sederhana dan mudah dibaca:
Dengan cara ini Anda tidak akan mendapatkan potongan parsial terakhir. Jika Anda ingin mendapatkan
(9, None, None, None)
potongan terakhir, cukup gunakanizip_longest
dariitertools
.sumber
zip(*([it]*4))
Jika Anda tidak keberatan menggunakan paket eksternal, Anda dapat menggunakan
iteration_utilities.grouper
dari 1 . Ini mendukung semua iterables (bukan hanya urutan):iteration_utilties
yang mencetak:
Jika panjangnya bukan kelipatan dari ukuran grup, itu juga mendukung pengisian (grup terakhir yang tidak lengkap) atau pemotongan (membuang grup terakhir yang tidak lengkap) yang terakhir:
Tolak ukur
Saya juga memutuskan untuk membandingkan run-time dari beberapa pendekatan yang disebutkan. Ini adalah log-log plot pengelompokan menjadi grup elemen "10" berdasarkan daftar ukuran yang berbeda-beda. Untuk hasil kualitatif: Turunkan berarti lebih cepat:
Setidaknya dalam benchmark ini
iteration_utilities.grouper
berkinerja terbaik. Diikuti oleh pendekatan Craz .Benchmark dibuat dengan 1 . Kode yang digunakan untuk menjalankan tolok ukur ini adalah:
simple_benchmark
1 Penafian: Saya penulis perpustakaan
iteration_utilities
dansimple_benchmark
.sumber
Karena belum ada yang menyebutkannya, berikut ini
zip()
solusinya:Ini hanya berfungsi jika panjang urutan Anda selalu dapat dibagi dengan ukuran chunk atau Anda tidak peduli dengan trailing chunk jika tidak.
Contoh:
Atau menggunakan itertools.izip untuk mengembalikan iterator, bukan daftar:
Padding dapat diperbaiki menggunakan jawaban @ ::
sumber
Menggunakan map () alih-alih zip () memperbaiki masalah padding dalam jawaban JF Sebastian:
Contoh:
sumber
itertools.izip_longest
(Py2) /itertools.zip_longest
(Py3); penggunaan inimap
sudah dua kali ditinggalkan, dan tidak tersedia di Py3 (Anda tidak bisa lulusNone
sebagai fungsi mapper, dan berhenti ketika iterable terpendek habis, bukan yang terpanjang; itu tidak pad).Pendekatan lain adalah dengan menggunakan bentuk dua argumen
iter
:Ini dapat disesuaikan dengan mudah untuk menggunakan bantalan (ini mirip dengan jawaban Markus Jarderot ):
Ini bahkan dapat dikombinasikan untuk pengisi opsional:
sumber
Jika daftar besar, cara berkinerja terbaik untuk melakukannya adalah dengan menggunakan generator:
sumber
iterable = range(100000000)
&chunksize
hingga 10000.Menggunakan sedikit fungsi dan hal-hal yang sebenarnya tidak menarik bagi saya; Saya lebih suka menggunakan irisan:
sumber
len
. Anda dapat melakukan tes denganitertools.repeat
atauitertools.cycle
.[...for...]
daftar untuk secara fisik membangun daftar alih-alih menggunakan(...for...)
ekspresi generator yang hanya akan peduli dengan elemen berikutnya dan memori cadanganUntuk menghindari semua konversi ke daftar
import itertools
dan:Menghasilkan:
Saya memeriksa
groupby
dan tidak dikonversi ke daftar atau digunakanlen
jadi saya (berpikir) ini akan menunda resolusi dari setiap nilai sampai benar-benar digunakan. Sayangnya tidak ada jawaban yang tersedia (saat ini) yang menawarkan variasi ini.Tentunya jika Anda perlu menangani setiap item pada gilirannya untuk loop atas g:
Minat khusus saya dalam hal ini adalah kebutuhan untuk menggunakan generator untuk mengirimkan perubahan dalam batch hingga 1000 ke API gmail:
sumber
groupby(messages, lambda x: x/3)
akan memberi Anda TypeError (untuk mencoba membagi string dengan int), bukan pengelompokan 3 huruf. Sekarang jika Anda melakukannya,groupby(enumerate(messages), lambda x: x[0]/3)
Anda mungkin memiliki sesuatu. Tetapi Anda tidak mengatakan itu di posting Anda.Dengan NumPy itu sederhana:
keluaran:
sumber
sumber
Kecuali saya melewatkan sesuatu, solusi sederhana berikut dengan ekspresi generator belum disebutkan. Ini mengasumsikan bahwa baik ukuran dan jumlah bongkahan diketahui (yang sering terjadi), dan bahwa tidak ada padding diperlukan:
sumber
Dalam metode kedua Anda, saya akan maju ke grup 4 berikutnya dengan melakukan ini:
Namun, saya belum melakukan pengukuran kinerja jadi saya tidak tahu mana yang lebih efisien.
Karena itu, saya biasanya akan memilih metode pertama. Itu tidak cantik, tapi itu sering kali akibat dari berinteraksi dengan dunia luar.
sumber
Namun jawaban lain, kelebihannya adalah:
1) Mudah dimengerti
2) Bekerja pada setiap iterable, bukan hanya urutan (beberapa jawaban di atas akan tersedak filehandles)
3) Tidak memuat potongan ke memori sekaligus
4) Tidak membuat daftar panjang referensi chunk untuk iterator yang sama dalam memori
5) Tidak ada padding nilai mengisi pada akhir daftar
Yang sedang berkata, saya belum waktunya sehingga mungkin lebih lambat daripada beberapa metode yang lebih pintar, dan beberapa keuntungan mungkin tidak relevan mengingat use case.
Pembaruan:
Beberapa kelemahan karena fakta loop dalam dan luar menarik nilai dari iterator yang sama:
1) terus tidak bekerja seperti yang diharapkan di loop luar - itu hanya melanjutkan ke item berikutnya daripada melewatkan sepotong . Namun, ini sepertinya bukan masalah karena tidak ada yang bisa diuji di loop luar.
2) istirahat tidak bekerja seperti yang diharapkan dalam loop dalam - kontrol akan berakhir di loop dalam lagi dengan item berikutnya di iterator. Untuk melewati potongan utuh, bungkus bagian dalam iterator (ii di atas) dalam tuple, misalnya
for c in tuple(ii)
, atau atur bendera dan buang knalpotnya.sumber
sumber
Anda dapat menggunakan fungsi partisi atau potongan dari pustaka funcy :
Fungsi-fungsi ini juga memiliki versi iterator
ipartition
danichunks
, yang akan lebih efisien dalam hal ini.Anda juga dapat mengintip implementasinya .
sumber
Tentang solusi yang diberikan di
J.F. Sebastian
sini :Ini pintar, tetapi memiliki satu kelemahan - selalu mengembalikan tuple. Bagaimana cara mendapatkan string?
Tentu saja Anda dapat menulis
''.join(chunker(...))
, tetapi tuple sementara tetap dibuat.Anda dapat menyingkirkan tuple sementara dengan menulis sendiri
zip
, seperti ini:Kemudian
Contoh penggunaan:
sumber
zip
alih-alih menggunakan yang sudah ada tampaknya bukan ide terbaik.Saya suka pendekatan ini. Rasanya sederhana dan tidak ajaib dan mendukung semua jenis iterable dan tidak memerlukan impor.
sumber
Saya tidak pernah ingin potongan saya empuk, sehingga persyaratan sangat penting. Saya menemukan bahwa kemampuan untuk bekerja pada setiap iterable juga merupakan persyaratan. Karena itu, saya memutuskan untuk memperluas jawaban yang diterima, https://stackoverflow.com/a/434411/1074659 .
Kinerja mengambil sedikit hit dalam pendekatan ini jika padding tidak diinginkan karena kebutuhan untuk membandingkan dan memfilter nilai padded. Namun, untuk ukuran bongkahan besar, utilitas ini sangat performant.
sumber
Berikut adalah chunker tanpa impor yang mendukung generator:
Contoh penggunaan:
sumber
Dengan Python 3.8 Anda dapat menggunakan operator walrus dan
itertools.islice
.sumber
Sepertinya tidak ada cara yang bagus untuk melakukan ini. Berikut adalah halaman yang memiliki sejumlah metode, termasuk:
sumber
Jika daftar memiliki ukuran yang sama, Anda dapat menggabungkannya ke dalam daftar 4-tupel
zip()
. Sebagai contoh:Inilah yang
zip()
dihasilkan fungsi:Jika daftar besar, dan Anda tidak ingin menggabungkannya ke daftar yang lebih besar, gunakan
itertools.izip()
, yang menghasilkan iterator, bukan daftar.sumber
One-liner, solusi adhoc untuk beralih ke daftar
x
dalam potongan ukuran4
-sumber