Cách tốt nhất để tạo danh sách "đảo ngược" trong Python?


89

Trong Python, cách tốt nhất để tạo một danh sách mới có các mục giống với các mục của một số danh sách khác, nhưng theo thứ tự ngược lại là gì? (Tôi không muốn sửa đổi danh sách hiện có.)

Đây là một giải pháp đã xảy ra với tôi:

new_list = list(reversed(old_list))

Cũng có thể sao chép old_listsau đó đảo ngược bản sao tại chỗ:

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

Có lựa chọn nào tốt hơn mà tôi đã bỏ qua không? Nếu không, có lý do thuyết phục nào (chẳng hạn như hiệu quả) để sử dụng một trong các cách tiếp cận ở trên so với cách tiếp cận khác không?

Câu trả lời:


205
newlist = oldlist[::-1]

Các [::-1]cắt (mà vợ tôi Anna thích gọi là "cười sao Hỏa" ;-) phương tiện: lát toàn bộ trình tự, với một bước -1, tức là ngược lại. Nó hoạt động cho tất cả các trình tự.

Lưu ý rằng điều này ( các lựa chọn thay thế bạn đã đề cập) tương đương với "bản sao cạn", tức là: nếu các mục có thể thay đổi và bạn gọi chúng là đột biến trên chúng, thì các đột biến trong các mục được giữ trong danh sách ban đầu cũng nằm trong các mục trong danh sách đảo ngược, và ngược lại. Nếu bạn cần tránh điều đó, a copy.deepcopy(mặc dù luôn là một hoạt động tiềm ẩn tốn kém), theo sau trong trường hợp này là a .reverse, là lựa chọn tốt duy nhất.


Ôi trời! Đó là sự thanh lịch. Cho đến một vài ngày trước, tôi đã không nhận ra rằng có thể bao gồm một "bước" khi cắt; Bây giờ tôi tự hỏi làm thế nào tôi có thể vượt qua mà không có nó! Cảm ơn, Alex. :)
davidchambers 14/09/10

1
Cảm ơn bạn cũng đã đề cập rằng điều này tạo ra một bản sao nông. Tuy nhiên, đó là tất cả những gì tôi cần, vì vậy tôi sắp thêm một mặt cười của sao Hỏa vào mã của mình.
davidchambers 14/09/10

13
Điều này thực sự có vẻ kỳ diệu hơn rất nhiều và khó đọc hơn rất nhiều so với gợi ý ban đầu , list(reversed(oldlist)). Ngoài một tối ưu hóa vi mô nhỏ, có lý do gì để thích [::-1]làm reversed()không?
Brian Campbell

@BrianCampbell: Điều gì "kỳ diệu" về nó? Nếu bạn hiểu về cách cắt, nó hoàn toàn có ý nghĩa. Nếu bạn không hiểu về cắt ... tốt, bạn thực sự nên học nó từ khá sớm trong sự nghiệp Python của mình. Tất nhiên reversedcó một lợi thế rất lớn khi bạn không cần danh sách, bởi vì nó không lãng phí bộ nhớ hoặc thời gian đầu tư xây dựng danh sách. Nhưng khi bạn làm cần danh sách, sử dụng [::-1]thay vì list(reversed())là tương tự như sử dụng một [listcomp]thay vì list(genexpr).
abarnert

10
@abarnert Trong đoạn mã mà tôi làm việc, tôi rất hiếm khi nhìn thấy đối số lát thứ ba, nếu tôi thấy công dụng của nó, tôi phải tìm hiểu ý nghĩa của nó. Một khi tôi làm như vậy, vẫn không rõ ràng rằng các giá trị bắt đầu và kết thúc mặc định được hoán đổi khi bước là âm. Thoạt nhìn, mà không cần tra cứu ý nghĩa của đối số thứ ba, tôi có thể đoán điều đó [::-1]có nghĩa là loại bỏ phần tử cuối cùng của danh sách, thay vì đảo ngược nó. reversed(list)nói chính xác những gì nó đang làm; nó thể hiện ý định của nó, theo nghĩa "Rõ ràng tốt hơn là ngầm hiểu", "Số lượng khả năng đọc" và "Rẻ hơn là dày đặc".
Brian Campbell

56

Bây giờ chúng ta hãy timeit. Gợi ý: Alex's [::-1]là nhanh nhất :)

$ 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

Cập nhật: Đã thêm phương pháp comp danh sách được đề xuất bởi ins InspectorG4dget. Tôi sẽ để kết quả tự nói.


8
Chỉ cần một lưu ý - đây là chính xác để tạo danh sách bản sao đảo ngược nhưng ngược lại vẫn còn hiệu quả hơn cho lần lặp sau đó[::-1]
Tadhg McDonald-Jensen

7

Điều chỉnh

Nó đáng để cung cấp một điểm chuẩn / điều chỉnh cơ sở cho các tính toán thời gian bằng sdolan cho thấy hiệu suất 'đảo ngược' mà không cần list()chuyển đổi thường xuyên không cần thiết . Thao list()tác này thêm 26 usec bổ sung vào thời gian chạy và chỉ cần thiết trong trường hợp không chấp nhận được trình lặp.

Các kết quả:

reversed(lst) -- 11.2 usecs

list(reversed(lst)) -- 37.1 usecs

lst[::-1] -- 23.6 usecs

Tính toán:

# 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

Kết luận:

Kết luận của các bài kiểm tra reversed()này nhanh hơn lát cắt [::-1]12,4 usec


15
đảo ngược () trả về một đối tượng trình vòng lặp được đánh giá lười biếng, vì vậy tôi nghĩ rằng nó không phải là một so sánh công bằng với ký hiệu cắt [:: - 1] nói chung.
óng ánh

1
Ngay cả trong trường hợp có thể sử dụng trực tiếp trình lặp, chẳng hạn như ''.join(reversed(['1','2','3'])), phương thức slice vẫn nhanh hơn> 30%.
dansalmo

1
Không thắc mắc tại sao bạn lại nhận được kết quả giống nhau từ 2 bài kiểm tra đầu tiên: chúng giống hệt nhau!
MestreLion

Kết quả của tôi gần giống với điều này hơn. ol [:: - 1] có độ dài gấp đôi danh sách (đảo ngược (ol)). Phương thức ol [:: - 1] cần viết ít ký tự hơn. Tuy nhiên, danh sách (đảo ngược (ol)) có lẽ dễ đọc hơn đối với các lập trình viên python mới bắt đầu và nó nhanh hơn trên máy của tôi.
dhj
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.