วิธีที่ดีที่สุดในการสร้างรายการ "ย้อนกลับ" ใน Python?


92

ใน Python วิธีใดที่ดีที่สุดในการสร้างรายการใหม่ที่มีรายการเหมือนกับรายการอื่น ๆ แต่เรียงลำดับย้อนกลับ (ฉันไม่ต้องการแก้ไขรายการที่มีอยู่)

นี่คือทางออกหนึ่งที่เกิดขึ้นกับฉัน:

new_list = list(reversed(old_list))

นอกจากนี้ยังสามารถทำซ้ำold_listจากนั้นย้อนกลับรายการที่ซ้ำกันได้:

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

มีตัวเลือกที่ดีกว่าที่ฉันเคยมองข้ามไปหรือไม่? หากไม่เป็นเช่นนั้นมีเหตุผลที่น่าสนใจ (เช่นประสิทธิภาพ) ที่จะใช้วิธีใดวิธีหนึ่งข้างต้นมากกว่าอีกวิธีหนึ่งหรือไม่?

คำตอบ:


208
newlist = oldlist[::-1]

การ[::-1]หั่น (ซึ่งแอนนาภรรยาของฉันชอบเรียกว่า "ดาวอังคารยิ้ม" ;-) หมายถึง: หั่นลำดับทั้งหมดโดยมีขั้นตอน -1 คือในทางกลับกัน ใช้ได้กับทุกลำดับ

โปรดทราบว่าสิ่งนี้ ( และทางเลือกอื่นที่คุณกล่าวถึง) เทียบเท่ากับ "สำเนาตื้น" กล่าวคือ: หากรายการนั้นไม่แน่นอนและคุณเรียกว่า mutators กับสิ่งเหล่านี้การกลายพันธุ์ในรายการที่อยู่ในรายการต้นฉบับจะอยู่ในรายการใน รายการที่กลับรายการและในทางกลับกัน หากคุณต้องการหลีกเลี่ยงสิ่งนั้น a copy.deepcopy(ในขณะที่การดำเนินการอาจมีราคาแพงเสมอ) ตามด้วย a .reverseเป็นตัวเลือกเดียวที่ดีในกรณีนี้


พุทโธ่! นั่นคือความสง่างาม เมื่อไม่กี่วันที่ผ่านมาฉันไม่รู้ว่ามันเป็นไปได้ที่จะรวม "ขั้นตอน" เมื่อหั่น; ตอนนี้ฉันสงสัยว่าฉันเคยผ่านมันมาได้อย่างไร! ขอบคุณอเล็กซ์ :)
davidchambers

1
ขอบคุณเช่นกันที่ระบุว่าสิ่งนี้สร้างสำเนาที่ไม่ชัดเจน นั่นคือทั้งหมดที่ฉันต้องการดังนั้นฉันกำลังจะเพิ่ม Martian smiley ในรหัสของฉัน
davidchambers

13
สิ่งนี้ดูเหมือนจะวิเศษกว่ามากและอ่านได้น้อยกว่าข้อเสนอแนะเดิมlist(reversed(oldlist))มาก อื่น ๆ กว่าเล็กน้อยไมโครเพิ่มประสิทธิภาพจะมีเหตุผลใด ๆ จะชอบ[::-1]ไปreversed()?
Brian Campbell

@BrianCampbell: "เวทมนตร์" เกี่ยวกับมันคืออะไร? ถ้าคุณเข้าใจการแบ่งส่วนทั้งหมดมันก็สมเหตุสมผลดี หากคุณไม่เข้าใจการแบ่งส่วน ... คุณควรเรียนรู้มันตั้งแต่เนิ่นๆในอาชีพ Python ของคุณ แน่นอนว่าreversedมีข้อได้เปรียบอย่างมากเมื่อคุณไม่ต้องการรายการเพราะไม่ต้องเสียหน่วยความจำหรือเสียเวลาในการสร้าง แต่เมื่อคุณไม่จำเป็นต้องมีรายการที่ใช้[::-1]แทนการlist(reversed())จะคล้ายคลึงกับการใช้แทน[listcomp] list(genexpr)
ยกเลิก

11
@abarnert ในรหัสที่ฉันทำงานด้วยฉันจึงไม่ค่อยเห็นอาร์กิวเมนต์ชิ้นที่สามถ้าฉันเห็นการใช้งานฉันต้องค้นหาความหมาย เมื่อฉันทำแล้วก็ยังไม่ชัดเจนว่าค่าเริ่มต้นและค่าสิ้นสุดเริ่มต้นจะถูกสลับเมื่อขั้นตอนเป็นลบ เมื่อมองอย่างรวดเร็วโดยไม่ต้องค้นหาความหมายของอาร์กิวเมนต์ที่สามฉันอาจเดาได้ว่านั่น[::-1]หมายถึงการลบองค์ประกอบสุดท้ายของรายการแทนที่จะย้อนกลับ reversed(list)ระบุว่ากำลังทำอะไรอยู่ มันสะกดเจตนาของมันในแง่ของ "Explicit is better than implicit", "Readability counts" และ "Sparse is better than density"
Brian Campbell

56

timeitตอนนี้ขอ คำแนะนำ: Alex [::-1]เร็วที่สุด :)

$ 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

อัปเดต:เพิ่มวิธีการคำนวณรายการที่แนะนำโดย inspectorG4dget ฉันจะให้ผลลัพธ์พูดเอง


8
หมายเหตุ - นี่เป็นสิ่งที่ถูกต้องสำหรับการสร้างรายการสำเนาย้อนกลับ แต่การย้อนกลับยังคงมีประสิทธิภาพมากกว่าสำหรับการทำซ้ำในตอนนั้น[::-1]
Tadhg McDonald-Jensen

7

การปรับเปลี่ยน

เป็นสิ่งที่ควรค่าแก่การจัดทำเกณฑ์มาตรฐาน / การปรับค่าสำหรับการคำนวณเวลาโดย sdolan ซึ่งแสดงประสิทธิภาพของ 'ย้อนกลับ' โดยไม่มีการlist()แปลงที่ไม่จำเป็นบ่อยครั้ง list()การดำเนินการนี้จะเพิ่ม 26 usecs เพิ่มเติมให้กับรันไทม์และจำเป็นเฉพาะในกรณีที่ตัววนซ้ำไม่สามารถยอมรับได้

ผล:

reversed(lst) -- 11.2 usecs

list(reversed(lst)) -- 37.1 usecs

lst[::-1] -- 23.6 usecs

การคำนวณ:

# 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

สรุป:

การสรุปผลการทดสอบเหล่านี้reversed()เร็วกว่าการแบ่งชิ้น[::-1]โดย 12.4 usecs


15
reverseed () ส่งคืนอ็อบเจ็กต์ตัววนซ้ำซึ่งมีการประเมินแบบขี้เกียจดังนั้นฉันคิดว่ามันไม่ใช่การเปรียบเทียบที่ยุติธรรมกับสัญกรณ์การแบ่งส่วน [:: - 1] โดยทั่วไป
สีรุ้ง

1
แม้ในกรณีที่สามารถใช้ตัววนซ้ำได้โดยตรงเช่น''.join(reversed(['1','2','3']))เมธอด slice ก็ยังเร็วกว่า> 30%
dansalmo

1
ไม่แปลกใจเลยว่าทำไมคุณถึงได้ผลลัพธ์เดียวกันจากการทดสอบ 2 ครั้งแรก: มันเหมือนกัน!
MestreLion

ผลลัพธ์ของฉันใกล้เคียงกันมากขึ้น ol [:: - 1] ใช้เวลาประมาณสองเท่าของรายการ (ย้อนกลับ (ol)) เมธอด ol [:: - 1] ใช้อักขระน้อยลงในการเขียน อย่างไรก็ตามรายการ (ย้อนกลับ (ol)) น่าจะอ่านได้มากกว่าสำหรับโปรแกรมเมอร์ python ระดับเริ่มต้นและเร็วกว่าในเครื่องของฉัน
dhj
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.