Cara Pythonic untuk mengembalikan daftar setiap item ke-n dalam daftar yang lebih besar

170

Katakanlah kita memiliki daftar angka dari 0 hingga 1000. Apakah ada cara pythonic / efisien untuk menghasilkan daftar item ke-10 pertama dan selanjutnya, yaitu [0, 10, 20, 30, ... ]?

Ya, saya bisa melakukan ini menggunakan for loop, tapi saya bertanya-tanya apakah ada cara yang lebih rapi untuk melakukan ini, mungkin bahkan dalam satu baris?

p.brown
sumber

Jawaban:

289
>>> lst = list(range(165))
>>> lst[0::10]
[0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160]

Perhatikan bahwa ini sekitar 100 kali lebih cepat dari perulangan dan memeriksa modulus untuk setiap elemen:

$ python -m timeit -s "lst = list(range(1000))" "lst1 = [x for x in lst if x % 10 == 0]"
1000 loops, best of 3: 525 usec per loop
$ python -m timeit -s "lst = list(range(1000))" "lst1 = lst[0::10]"
100000 loops, best of 3: 4.02 usec per loop
Ned Deily
sumber
4
Tentu, pemahaman daftar lebih kuat secara umum. OTOH, pertanyaannya menempatkan daftar yang ada dan, dalam hal ini, sepotong berfungsi dengan baik.
Ned Deily
Saya mengomentari ini di bawah ini dalam daftar jawaban pemahaman. Hati-hati dengan "jika x% 10 == 0". Ini hanya berfungsi dengan contoh daftar khusus ini, tetapi jika daftar input misalnya l = range (0,1000,2) ia tidak akan mengeluarkan setiap item ke-10.
Andre Miller
12
@Andre: sangat benar. Jadi ini adalah contoh fitur bahasa sederhana, operator slice, yang ternyata dalam kasus ini (1) untuk membuatnya lebih mudah untuk mendapatkan hasil yang benar; (2) menghasilkan ekspresi yang lebih ringkas; dan (3) lebih cepat 2 kali lipat. (1) sejauh ini menjadi perhatian yang paling penting, tentu saja, tetapi, berkat desain dan implementasi bahasa yang cermat, Anda mendapatkan ketiganya dengan harga 1. Pertanyaan dan tanggapan yang bagus.
Ned Deily
2
Itu 0berlebihan di l[0::10]. l[::10]lebih mudah dibaca, kurang membingungkan.
Konstantin Schubert
Saya terkejut dengan perbandingan kinerja 0,5 detik untuk pemahaman daftar dan 0,4 untuk slice daftar. Tampak sangat lambat, mengapa daftar slicing membutuhkan 100 ribu loop untuk daftar ukuran 1 ribu !?
Damo
57
  1. source_list[::10] adalah yang paling jelas, tetapi ini tidak berhasil untuk setiap iterable dan tidak efisien untuk daftar besar.
  2. itertools.islice(source_sequence, 0, None, 10) bekerja untuk semua iterable dan hemat memori, tetapi mungkin bukan solusi tercepat untuk daftar besar dan langkah besar.
  3. (source_list[i] for i in xrange(0, len(source_list), 10))
Denis Otkidach
sumber
1
+1 Jawaban terbaik, IMO. Ketiga proposal solusi umum (mis. Ambil daftar sumber sebagai yang diberikan). Solusi generator (3.) bagus karena menyaring pada indeks daftar sumber. Mungkin memori seefisien 2. Baik indeks dan daftar hasil adalah generator dan dengan demikian dibangun dengan malas, yang juga mungkin yang tercepat jika Anda tidak memerlukan daftar hasil dalam satu potongan. Hanya jika daftar sumber bisa menjadi generator saya akan pergi dengan "item" Paul, saya menghitung (l) "idiom, karena tidak ada len () dari generator. BTW, iterable macam apa yang tidak akan bekerja dengan 1.? Generator ?!
ThomasH
Iterable = objek dengan metode __iter __ () mengembalikan iterator (objek dengan metode next ())
Denis Otkidach
24

Anda dapat menggunakan operator slice seperti ini:

l = [1,2,3,4,5]
l2 = l[::2] # get subsequent 2nd item
Nick Dandoulakis
sumber
bagaimana cara mendapatkan setiap item ke-2 mulai dari tanggal 3?
user1993
4
@ user1993L[2::2]
Nick Dandoulakis
19

Dari manual: s[i:j:k] slice of s from i to j with step k

li = range(100)
sub = li[0::10]

>>> sub
[0, 10, 20, 30, 40, 50, 60, 70, 80, 90]
tuergeist
sumber
13
newlist = oldlist[::10]

Ini memilih setiap elemen ke-10 dari daftar.

David Z
sumber
4

Mengapa tidak menggunakan parameter langkah fungsi rentang juga untuk mendapatkan:

l = range(0, 1000, 10)

Sebagai perbandingan, di mesin saya:

H:\>python -m timeit -s "l = range(1000)" "l1 = [x for x in l if x % 10 == 0]"
10000 loops, best of 3: 90.8 usec per loop
H:\>python -m timeit -s "l = range(1000)" "l1 = l[0::10]"
1000000 loops, best of 3: 0.861 usec per loop
H:\>python -m timeit -s "l = range(0, 1000, 10)"
100000000 loops, best of 3: 0.0172 usec per loop
mobil van
sumber
3
@ SilentGhost: Itu benar, tetapi karena ini adalah pertanyaan pemula, fungsi rentang mungkin adalah apa yang benar - benar ingin mereka lakukan, jadi saya pikir itu adalah jawaban yang valid. (Meskipun batas atas seharusnya 1001, bukan 1000)
Scott Griffiths
2
existing_list = range(0, 1001)
filtered_list = [i for i in existing_list if i % 10 == 0]

sumber
1
mengapa Anda memiliki klausa if ketika rentang (0, 1001, 10) sudah hanya mengambil setiap elemen ke-10?
Autoplectic
4
Komentar yang sama di sini, ini tidak memecahkan masalah yang lebih umum dari "cara Pythonic untuk mengembalikan daftar setiap item ke-n dalam daftar yang lebih besar" solusi Anda tergantung pada kenyataan bahwa nilai-nilai daftar contoh adalah 0 hingga 1000 dan hanya menarik item keluar dari daftar yang memiliki nilai habis dibagi 10 alih-alih setiap item ke-10.
Andre Miller
1
Nah, OP menulis: "kami memiliki daftar angka dari nol hingga 1000". Jadi dia tidak membutuhkan solusi umum.
1
Dia menulis 'Katakan kita punya ..' yang menyiratkan hanya sebuah contoh. Jika dia benar-benar ingin setiap angka 10 dari daftar nol hingga 1000 maka jawabannya akan berkisar (0,1001,10) atau yang serupa.
Andre Miller
1

Berikut ini adalah implementasi yang lebih baik dari pemahaman daftar "setiap item ke-10", yang tidak menggunakan konten daftar sebagai bagian dari tes keanggotaan:

>>> l = range(165)
>>> [ item for i,item in enumerate(l) if i%10==0 ]
[0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160]
>>> l = list("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
>>> [ item for i,item in enumerate(l) if i%10==0 ]
['A', 'K', 'U']

Tapi ini masih jauh lebih lambat daripada hanya menggunakan daftar slicing.

PaulMcG
sumber
-9

Pemahaman daftar persis dibuat untuk itu:

smaller_list = [x for x in range(100001) if x % 10 == 0]

Anda bisa mendapatkan lebih banyak info tentang mereka di dokumentasi resmi python: http://docs.python.org/tutorial/datastructures.html#list-comprehensions

Santi
sumber
Batas atas harus 1000, bukan 10000. Solusi Anda tidak termasuk batas atas 1000 karena rentang berhenti pada 999. +1 untuk tautan ke pemahaman daftar.
19
Ini tidak benar-benar menarik setiap item ke-10, itu menarik setiap item yang memiliki nilai habis dibagi 10. Dalam contoh khusus ini hal yang sama, tetapi mungkin tidak.
Andre Miller