Cara terbaik untuk membuat daftar "terbalik" dengan Python?

92

Dengan Python, apa cara terbaik untuk membuat daftar baru yang itemnya sama dengan daftar lainnya, tetapi dalam urutan terbalik? (Saya tidak ingin mengubah daftar yang sudah ada.)

Inilah satu solusi yang terpikir oleh saya:

new_list = list(reversed(old_list))

Ini juga memungkinkan untuk menduplikasi old_listkemudian membalikkan duplikat di tempat:

new_list = list(old_list) # or `new_list = old_list[:]`
new_list.reverse()

Apakah ada opsi yang lebih baik yang saya abaikan? Jika tidak, apakah ada alasan yang memaksa (seperti efisiensi) untuk menggunakan salah satu pendekatan di atas daripada yang lain?

davidchambers.dll
sumber

Jawaban:

208
newlist = oldlist[::-1]

The [::-1]mengiris (yang suka menyebutnya "smiley Mars" ;-) berarti istri saya Anna: slice seluruh urutan, dengan langkah -1, yaitu, secara terbalik. Ini berfungsi untuk semua urutan.

Perhatikan bahwa ini ( dan alternatif yang Anda sebutkan) sama dengan "salinan dangkal", yaitu: jika item bisa berubah dan Anda memanggil mutator padanya, mutasi pada item yang disimpan di daftar asli juga ada di item di daftar terbalik, dan sebaliknya. Jika Anda perlu menghindarinya, a copy.deepcopy(meskipun selalu merupakan operasi yang berpotensi mahal), diikuti dalam kasus ini dengan a .reverse, adalah satu-satunya pilihan yang baik.

Alex Martelli
sumber
Astaga! Itu adalah elegan. Sampai beberapa hari yang lalu saya tidak menyadari bahwa mungkin untuk menyertakan "langkah" saat mengiris; sekarang saya bertanya-tanya bagaimana saya bisa bertahan tanpanya! Terima kasih, Alex. :)
davidchambers
1
Terima kasih juga, karena menyebutkan bahwa ini menghasilkan salinan yang dangkal. Hanya itu yang saya butuhkan, jadi saya akan menambahkan smiley Mars ke kode saya.
davidchambers
13
Ini benar-benar tampak jauh lebih ajaib dan jauh lebih sulit dibaca daripada saran asli , list(reversed(oldlist)). Selain kecil mikro-optimasi, apakah ada alasan untuk lebih memilih [::-1]ke reversed()?
Brian Campbell
@ BrianCampbell: Apa "keajaiban" tentang itu? Jika Anda memahami mengiris sama sekali, itu sangat masuk akal. Jika Anda tidak mengerti mengiris… yah, Anda benar-benar harus mempelajarinya sejak awal dalam karir Python Anda. Tentu saja reversedmemiliki keuntungan besar ketika Anda tidak membutuhkan daftar tersebut, karena tidak membuang-buang memori atau membangunnya di awal. Tetapi ketika Anda benar- benar membutuhkan daftar tersebut, menggunakan [::-1]sebagai ganti dari list(reversed())sama dengan menggunakan a, [listcomp]bukan list(genexpr).
abarnert
11
@abarnert Dalam kode yang saya gunakan, saya sangat jarang melihat argumen potongan ketiga, jika saya melihat penggunaannya saya harus mencari tahu apa artinya. Setelah saya melakukannya, masih belum jelas bahwa nilai awal dan akhir default ditukar ketika langkahnya negatif. Sekilas, tanpa mencari arti dari argumen ketiga, saya mungkin menebak itu [::-1]berarti menghapus elemen terakhir dari sebuah daftar, daripada membaliknya. reversed(list)menyatakan dengan tepat apa yang dilakukannya; itu menjelaskan maksudnya, dalam arti "Eksplisit lebih baik daripada implisit", "Keterbacaan dihitung", dan "Jarang lebih baik daripada padat".
Brian Campbell
56

Sekarang ayo timeit. Petunjuk: Alex [::-1]tercepat :)

$ p -m timeit "ol = [1, 2, 3]; nl = list(reversed(ol))"
100000 loops, best of 3: 2.34 usec per loop

$ p -m timeit "ol = [1, 2, 3]; nl = list(ol); nl.reverse();"
1000000 loops, best of 3: 0.686 usec per loop

$ p -m timeit "ol = [1, 2, 3]; nl = ol[::-1];"
1000000 loops, best of 3: 0.569 usec per loop

$ p -m timeit "ol = [1, 2, 3]; nl = [i for i in reversed(ol)];"
1000000 loops, best of 3: 1.48 usec per loop


$ p -m timeit "ol = [1, 2, 3]*1000; nl = list(reversed(ol))"
10000 loops, best of 3: 44.7 usec per loop

$ p -m timeit "ol = [1, 2, 3]*1000; nl = list(ol); nl.reverse();"
10000 loops, best of 3: 27.2 usec per loop

$ p -m timeit "ol = [1, 2, 3]*1000; nl = ol[::-1];"
10000 loops, best of 3: 24.3 usec per loop

$ p -m timeit "ol = [1, 2, 3]*1000; nl = [i for i in reversed(ol)];"
10000 loops, best of 3: 155 usec per loop

Pembaruan: Menambahkan metode komp daftar yang disarankan oleh inspectorG4dget. Saya akan membiarkan hasilnya berbicara sendiri.

Sam Dolan
sumber
8
Sekadar catatan - ini akurat untuk membuat daftar salinan terbalik tetapi dibalik masih lebih efisien untuk iterasi[::-1]
Tadhg McDonald-Jensen
7

Penyesuaian

Ada baiknya memberikan patokan / penyesuaian dasar untuk perhitungan timeit oleh sdolan yang menunjukkan kinerja 'terbalik' tanpa list()konversi yang sering tidak perlu . list()Operasi ini menambahkan 26 usec tambahan ke runtime dan hanya diperlukan jika iterator tidak dapat diterima.

Hasil:

reversed(lst) -- 11.2 usecs

list(reversed(lst)) -- 37.1 usecs

lst[::-1] -- 23.6 usecs

Perhitungan:

# I ran this set of 100000 and came up with 11.2, twice:
python -m timeit "ol = [1, 2, 3]*1000; nl = reversed(ol)"
100000 loops, best of 3: 11.2 usec per loop

# This shows the overhead of list()
python -m timeit "ol = [1, 2, 3]*1000; nl = list(reversed(ol))"
10000 loops, best of 3: 37.1 usec per loop

# This is the result for reverse via -1 step slices
python -m timeit "ol = [1, 2, 3]*1000;nl = ol[::-1]"
10000 loops, best of 3: 23.6 usec per loop

Kesimpulan:

Kesimpulan dari pengujian reversed()ini lebih cepat daripada potongan [::-1]dengan 12,4 usec

mekarpeles
sumber
15
reversed () mengembalikan objek iterator yang dievaluasi secara malas, jadi menurut saya ini bukan perbandingan yang adil dengan notasi pemotongan [:: - 1] secara umum.
warnawarni
1
Bahkan dalam kasus di mana iterator dapat digunakan secara langsung, seperti ''.join(reversed(['1','2','3']))metode slice masih> 30% lebih cepat.
dansalmo
1
Tidak heran mengapa Anda mendapatkan hasil yang sama dari 2 tes pertama: keduanya identik!
MestreLion
Hasil saya lebih mirip dengan ini. ol [:: - 1] membutuhkan waktu dua kali lebih lama dari list (dibalik (ol)). Metode ol [:: - 1] membutuhkan lebih sedikit karakter untuk ditulis. Namun, daftar (terbalik (ol)) mungkin lebih mudah dibaca oleh pemrogram python pemula dan lebih cepat di komputer saya.
dhj