Python'da "tersine çevrilmiş" bir liste oluşturmanın en iyi yolu?


92

Python'da, öğeleri başka bir listeyle aynı olan, ancak ters sırada olan yeni bir liste oluşturmanın en iyi yolu nedir? (Mevcut listeyi yerinde değiştirmek istemiyorum.)

İşte aklıma gelen bir çözüm:

new_list = list(reversed(old_list))

old_listKopyayı yerinde çoğaltmak ve sonra tersine çevirmek de mümkündür :

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

Göz ardı ettiğim daha iyi bir seçenek var mı? Değilse, yukarıdaki yaklaşımlardan birini diğerine tercih etmek için zorlayıcı bir neden (verimlilik gibi) var mı?

Yanıtlar:


208
newlist = oldlist[::-1]

[::-1]Ters olarak -1 adımı, yani, ile, dilim bütün sekansı eşim Anna "Mars suratı" ;-) aracı aramaya sever dilimleme (. Tüm diziler için çalışır.

Bunun ( ve bahsettiğiniz alternatiflerin) bir "yüzeysel kopya" ile eşdeğer olduğuna dikkat edin, yani: öğeler değiştirilebilirse ve bunlara mutatörler çağırırsanız, orijinal listede tutulan öğelerdeki mutasyonlar da öğelerde bulunur. ters liste ve tersi. Bundan kaçınmanız gerekiyorsa, a copy.deepcopy(her zaman potansiyel olarak maliyetli bir işlem olsa da), bu durumda a .reverseile devam eden tek iyi seçenektir.


Aman! Yani bir şık. Birkaç gün öncesine kadar, dilimleme sırasında bir "adım" eklemenin mümkün olduğunu fark etmemiştim; şimdi onsuz nasıl idare ettiğimi merak ediyorum! Teşekkürler Alex. :)
davidchambers

1
Bunun sığ bir kopya oluşturduğundan bahsettiğiniz için de teşekkürler. Gerçi tek ihtiyacım olan bu, o yüzden koduma bir Marslı gülen surat eklemek üzereyim.
davidchambers

13
Bu gerçekten çok daha sihir ve orijinal öneriden çok daha az okunabilir görünüyor list(reversed(oldlist)). Küçük bir mikro optimizasyon dışında, tercih [::-1]etmek için herhangi bir neden var reversed()mı?
Brian Campbell

@BrianCampbell: Bunun "sihri" nedir? Dilimlemeyi anlıyorsanız, tamamen mantıklı. Dilimlemeyi anlamıyorsanız… peki, Python kariyerinizin oldukça başlarında öğrenmelisiniz. Elbette listeye ihtiyacınız olmadığındareversed büyük bir avantajı vardır , çünkü bu, hafızayı boşa harcamaz veya önceden oluşturmak için zaman kaybetmez. Ama ne zaman yapmak kullanarak, listesi gerekir yerine kullanarak benzer bir yerine . [::-1]list(reversed())[listcomp]list(genexpr)
abarnert

11
@abarnert Birlikte çalıştığım kodda üçüncü dilim argümanını çok nadir görüyorum, eğer bir kullanım görürsem ne anlama geldiğine bakmam gerekiyor. Bunu yaptıktan sonra, adım negatif olduğunda varsayılan başlangıç ​​ve bitiş değerlerinin değiştirildiği hala açık değil. Hızlı bir bakışta, üçüncü argümanın anlamına bakmadan [::-1], bunun bir listenin son öğesini tersine çevirmek yerine kaldırmak anlamına geldiğini tahmin edebilirim . reversed(list)tam olarak ne yaptığını belirtir; "Açık, örtükten daha iyidir", "Okunabilirlik sayılır" ve "Seyrek yoğun olmaktan iyidir" anlamında amacını açıklar.
Brian Campbell

56

Şimdi bakalım timeit. İpucu: Alex'in en [::-1]hızlısı :)

$ 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

Güncelleme: inspectorG4dget tarafından önerilen liste kompozisyon yöntemi eklendi. Sonuçların kendileri için konuşmasına izin vereceğim.


8
Sadece bir not - bu, tersine çevrilmiş kopya listesi oluşturmak için doğrudur, ancak tersine çevrilmesi, yineleme için hala daha etkilidir[::-1]
Tadhg McDonald-Jensen,

7

Ayarlamalar

Sdolan tarafından yapılan zaman hesaplamaları için, genellikle gereksiz list()dönüştürme olmaksızın 'tersine çevrilmiş' performansın gösterildiği bir temel ölçüt / ayarlama sağlamaya değer . Bu list()işlem, çalışma zamanına ek 26 usec ekler ve yalnızca bir yineleyicinin kabul edilemez olması durumunda gereklidir.

Sonuçlar:

reversed(lst) -- 11.2 usecs

list(reversed(lst)) -- 37.1 usecs

lst[::-1] -- 23.6 usecs

Hesaplamalar:

# 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

Sonuçlar:

Bu testlerin sonucu reversed(), dilimden [::-1]12.4 usec ile daha hızlıdır.


15
reversed (), tembel olarak değerlendirilen bir yineleyici nesnesi döndürür, bu nedenle genel olarak dilimleme notasyonu [:: - 1] ile adil bir karşılaştırma olmadığını düşünüyorum.
2013

1
Bir yineleyicinin doğrudan kullanılabildiği bir durumda bile ''.join(reversed(['1','2','3'])), dilim yöntemi hala>% 30 daha hızlıdır.
dansalmo

1
İlk 2 testten neden aynı sonucu aldığınıza şaşmamalı: bunlar aynı!
MestreLion

Sonuçlarım buna daha çok benziyor. ol [:: - 1] listenin iki katı kadar uzun sürer (tersine çevrilmiş (ol)). Ol [:: - 1] yöntemi yazmak için daha az karakter alır. Bununla birlikte, liste (tersine çevrilmiş (ol)) muhtemelen yeni başlayan python programcıları için daha okunabilir ve makinemde daha hızlı.
dhj
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.