Pythonic way เพื่อส่งคืนรายการของทุกรายการที่ n ในรายการที่ใหญ่ขึ้น


170

สมมติว่าเรามีรายการตัวเลขตั้งแต่ 0 ถึง 1,000 มีวิธีการแบบ pythonic / อย่างมีประสิทธิภาพในการผลิตรายการแรกและต่อมาทุก 10 รายการคือ[0, 10, 20, 30, ... ]อะไร?

ใช่ฉันสามารถทำสิ่งนี้ได้โดยใช้ for for loop แต่ฉันสงสัยว่ามีวิธีการทำแบบนี้ที่ neater อาจทำได้แม้แต่ในบรรทัดเดียวหรือไม่?

คำตอบ:


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]

โปรดทราบว่านี่เร็วกว่าการวนลูปและตรวจสอบโมดูลัสสำหรับแต่ละองค์ประกอบประมาณ 100 เท่า:

$ 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

4
แน่นอนว่าความเข้าใจในรายการนั้นมีประสิทธิภาพมากกว่าโดยทั่วไป OTOH คำถามจะแสดงรายการที่มีอยู่และในกรณีนั้นชิ้นงานก็ใช้ได้
เน็ด Deily

ฉันแสดงความคิดเห็นในรายการด้านล่างนี้ในรายการคำตอบความเข้าใจ ระวังด้วย "if x% 10 == 0" ใช้งานได้กับตัวอย่างรายการเฉพาะนี้เท่านั้น แต่หากรายการอินพุตเป็นตัวอย่างเช่น l = range (0,1000,2) จะไม่ดึงทุกรายการที่ 10
Andre Miller

12
@ อังเดร: จริงมาก ดังนั้นนี่คือตัวอย่างของคุณลักษณะภาษาที่ต่ำต้อยผู้ดำเนินการแบ่งชิ้นซึ่งปรากฎในกรณีนี้ (1) เพื่อให้ง่ายขึ้นในการได้รับผลลัพธ์ที่ถูกต้อง (2) ให้ผลลัพธ์ที่กระชับยิ่งขึ้น และ (3) จะมีขนาด 2 คำสั่งเร็วขึ้น (1) คือความกังวลที่สำคัญที่สุดซึ่งแน่นอนว่าด้วยการออกแบบและการนำภาษามาใช้อย่างรอบคอบคุณจะได้รับทั้งสามข้อในราคา 1. คำถามและคำตอบที่ดี
เน็ด Deily

2
อยู่ในซ้ำซ้อน0 อ่านง่ายขึ้นสับสนน้อยลง l[0::10]l[::10]
Konstantin Schubert

ฉันรู้สึกประหลาดใจโดยการเปรียบเทียบประสิทธิภาพ 0.5 วินาทีสำหรับ list comprehension และ 0.4s สำหรับ list slice ดูเหมือนว่าช้ามากทำไมการแบ่งรายชื่อจึงต้องการ 100,000 ลูปสำหรับรายการขนาด 1 พัน!
Damo

57
  1. source_list[::10] เห็นได้ชัดที่สุด แต่วิธีนี้ใช้ไม่ได้กับการทำซ้ำใด ๆ และไม่มีประสิทธิภาพสำหรับหน่วยความจำขนาดใหญ่
  2. itertools.islice(source_sequence, 0, None, 10) ใช้ได้กับทุก iterable และมีประสิทธิภาพของหน่วยความจำ แต่อาจไม่ใช่โซลูชันที่เร็วที่สุดสำหรับลิสต์ขนาดใหญ่และขั้นตอนที่ยิ่งใหญ่
  3. (source_list[i] for i in xrange(0, len(source_list), 10))

1
+1 คำตอบที่ดีที่สุด IMO ข้อเสนอทั้งสามนี้เป็นคำตอบทั่วไป (เช่นนำรายการแหล่งที่มาตามที่กำหนด) โซลูชันตัวสร้าง (3. ) นั้นดีเพราะมันกรองดัชนีของรายการแหล่งที่มา อาจเป็นหน่วยความจำที่มีประสิทธิภาพเท่ากับ 2 ทั้งดัชนีและรายการผลลัพธ์คือเครื่องกำเนิดไฟฟ้าและสร้างขึ้นอย่างเกียจคร้านซึ่งอาจเร็วที่สุดหากคุณไม่ต้องการรายการผลลัพธ์ในกลุ่มเดียว เฉพาะในกรณีที่รายการแหล่งข้อมูลอาจเป็นตัวสร้างที่ฉันจะไปกับ "รายการของพอลฉันในการแจกแจง (l)" สำนวนเนื่องจากไม่มี len () ของเครื่องกำเนิดไฟฟ้า BTW ชนิดไหน iterable จะไม่ทำงานกับ 1. ? เครื่องปั่นไฟ ?!
ThomasH

Iterable = วัตถุที่มี __iter __ () วิธีการคืนค่าตัววนซ้ำ (วัตถุที่มีวิธีถัดไป))
Denis Otkidach




4

ทำไมไม่เพียงแค่ใช้พารามิเตอร์ขั้นตอนของฟังก์ชันช่วงเช่นกันเพื่อรับ:

l = range(0, 1000, 10)

สำหรับการเปรียบเทียบบนเครื่องของฉัน:

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

3
@SilentGhost: นั่นเป็นความจริง แต่เป็นนี้เป็นคำถามที่เริ่มต้นการทำงานของช่วงอาจจะมีสิ่งที่พวกเขาจริงๆต้องการที่จะทำดังนั้นฉันคิดว่ามันเป็นคำตอบที่ถูกต้อง (แม้ว่าขีด จำกัด บนควรจะเป็น 1001 ไม่ได้ 1000)
สกอตต์ Griffiths

2
existing_list = range(0, 1001)
filtered_list = [i for i in existing_list if i % 10 == 0]

1
ทำไมคุณถึงมีประโยคถ้าช่วง (0, 1001, 10) แล้วจะใช้เวลาทุกองค์ประกอบที่ 10 เท่านั้น?
Autoplectic

4
ความคิดเห็นแบบเดียวกันที่นี่ไม่สามารถแก้ปัญหาทั่วไปของ "Pythonic way เพื่อส่งคืนรายการของทุกรายการในรายการที่ใหญ่กว่า" การแก้ปัญหาของคุณขึ้นอยู่กับข้อเท็จจริงที่ว่าค่าของรายการตัวอย่างคือ 0 ถึง 1,000 และดึงรายการเท่านั้น ออกจากรายการที่มีค่าหารด้วย 10 แทนทุกรายการที่ 10
Andre Miller

1
ทีนี้ OP เขียนว่า: "เรามีรายการตัวเลขตั้งแต่ศูนย์ถึง 1,000" ดังนั้นเขาจึงไม่จำเป็นต้องแก้ปัญหาทั่วไป

1
เขาเขียน 'เรามี .. ' ซึ่งแสดงถึงตัวอย่างของมัน ถ้าเขาต้องการทุก ๆ 10 หมายเลขจากรายการเป็นศูนย์ถึง 1,000 คำตอบจะอยู่ในช่วง (0,1001,10) หรือสิ่งที่คล้ายกัน
Andre Miller

1

นี่คือการดำเนินการที่ดีขึ้นของความเข้าใจในรายการ "ทุก ๆ 10 รายการ" ที่ไม่ได้ใช้เนื้อหารายการเป็นส่วนหนึ่งของการทดสอบการเป็นสมาชิก:

>>> 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']

แต่นี่ยังช้ากว่าการใช้การแบ่งรายการ


-9

รายการความเข้าใจถูกสร้างขึ้นมาเพื่อ:

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

คุณสามารถรับข้อมูลเพิ่มเติมเกี่ยวกับพวกเขาได้ในเอกสารราชการของไพ ธ อน: http://docs.python.org/tutorial/datastructures.html#list-comprehensions


ขอบเขตบนควรเป็น 1,000 ไม่ใช่ 10,000 โซลูชันของคุณไม่รวมขอบเขตสูงสุด 1,000 เนื่องจากช่วงหยุดที่ 999 +1 สำหรับลิงก์ไปยัง list-comprehension

19
สิ่งนี้ไม่ได้ดึงออกมาทุกรายการที่ 10 แต่จะดึงทุกรายการที่มีค่าหารด้วย 10 ในตัวอย่างนี้มันเหมือนกัน แต่มันอาจจะไม่ใช่
Andre Miller
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.