วิธี pythonic ในการทำบางสิ่งบางอย่าง N ครั้งโดยไม่มีตัวแปรดัชนี?


161

ทุกวันฉันรักหลามมากขึ้นเรื่อย ๆ

วันนี้ฉันกำลังเขียนโค้ดบางอย่างเช่น:

for i in xrange(N):
    do_something()

ฉันต้องทำอะไรซักอย่าง N ครั้ง แต่ในแต่ละครั้งไม่ได้ขึ้นอยู่กับค่าของi(ตัวแปรดัชนี) ฉันรู้ว่าฉันกำลังสร้างตัวแปรที่ฉันไม่เคยใช้ ( i) และฉันคิดว่า "แน่นอนว่ามันเป็นวิธีการทำสิ่งที่ยิ่งใหญ่กว่านี้โดยไม่จำเป็นต้องใช้ตัวแปรดัชนีที่ไร้ประโยชน์"

ดังนั้น ... คำถามคือ: คุณรู้วิธีการทำงานที่เรียบง่ายในวิธีที่สวยงามยิ่งขึ้น (pythonic) หรือไม่?


7
ฉันเพิ่งเรียนรู้เกี่ยวกับตัวแปร _ แต่อย่างอื่นฉันจะพิจารณาวิธีที่คุณทำมัน Pythonic ฉันไม่คิดว่าฉันเคยเห็นวิธีง่าย ๆ สำหรับการวนรอบทำอย่างอื่นอย่างน้อยในหลาม แม้ว่าฉันจะแน่ใจว่ามีกรณีการใช้งานเฉพาะที่คุณดูและพูดว่า "เดี๋ยวก่อนมันดูแย่" - แต่โดยทั่วไปแล้ว xrange เป็นวิธีที่ต้องการ (เท่าที่ฉันเคยเห็น)
Wayne Werner


5
หมายเหตุ: xrange ไม่มีอยู่ใน Python3 ใช้rangeแทน
John Henckel

คำตอบ:


110

วิธีที่เร็วกว่าการวนซ้ำเล็กน้อยxrange(N)คือ:

import itertools

for _ in itertools.repeat(None, N):
    do_something()

3
เร็วเท่าไหร่ Python 3.1 ยังคงมีความแตกต่างอยู่หรือไม่?
Hamish Grubijan

15
@Hamish: การทดสอบของฉันกับ 2.6 บอกว่าเร็วขึ้น 32% (23.2 เราเทียบกับ 17.6 เราสำหรับ N = 1,000) แต่นั่นเป็นเวลาจริง ๆ อย่างไรก็ตาม ฉันจะเริ่มต้นที่รหัสของ OP เพราะสามารถอ่านได้ทันที (สำหรับฉัน)
Mike Boers

3
ดีที่รู้เกี่ยวกับความเร็ว แน่นอนฉันสะท้อนความเชื่อมั่นของ Mike เกี่ยวกับรหัสของ OP ที่สามารถอ่านได้มากขึ้น
Wayne Werner

@ เวย์นฉันคิดว่านิสัยมันมีพลังมากจริงๆ - ยกเว้นความจริงที่ว่าคุณคุ้นเคยกับมันทำไมจะ "นับจาก 0 ถึง N-1 [[และไม่สนใจจำนวน]] ทุกครั้งที่ทำการนับ - การดำเนินการอิสระ "จะมีความชัดเจนมากกว่า" ทำซ้ำ N คูณการดำเนินการต่อไปนี้ "... ?
Alex Martelli

4
คุณแน่ใจหรือว่าความเร็วนั้นมีความเกี่ยวข้องจริงๆ ไม่ใช่หรือถ้าคุณทำสิ่งใดที่สำคัญในลูปนั้นมันอาจจะใช้เวลาหลายร้อยหรือหลายพันเท่าตามสไตล์การทำซ้ำที่คุณเลือก?
Henning

55

ใช้ตัวแปร _ ตามที่ฉันเรียนรู้เมื่อฉันถามคำถามนี้เช่น:

# A long way to do integer exponentiation
num = 2
power = 3
product = 1
for _ in xrange(power):
    product *= num
print product

6
ไม่ downvoter แต่มันอาจจะเป็นเพราะคุณกำลังหมายถึงการโพสต์อื่นแทนการเพิ่มรายละเอียดในคำตอบ
Downgoat

5
@ Downgoat: ขอบคุณสำหรับความคิดเห็น ที่กล่าวว่ามีไม่มากที่จะพูดเกี่ยวกับสำนวนนี้ จุดของฉันในการอ้างถึงโพสต์อื่นคือการชี้ให้เห็นว่าการค้นหาอาจทำให้คำตอบ ฉันพบว่าแดกดันที่คำถามนี้มี upvotes หลายครั้งเช่นกัน
GreenMatt

39

ฉันแค่ใช้for _ in range(n)มันตรงประเด็น มันจะสร้างรายการทั้งหมดสำหรับจำนวนมหาศาลใน Python 2 แต่ถ้าคุณใช้ Python 3 นั่นไม่ใช่ปัญหา


10

เนื่องจากฟังก์ชั่นเป็นพลเมืองชั้นหนึ่งคุณสามารถเขียนเสื้อคลุมเล็ก ๆ (จากคำตอบของ Alex)

def repeat(f, N):
    for _ in itertools.repeat(None, N): f()

จากนั้นคุณสามารถส่งผ่านฟังก์ชั่นเป็นอาร์กิวเมนต์


@Hamish: เกือบไม่มีอะไร (17.8 เราต่อลูปภายใต้เงื่อนไขเดียวกันกับการกำหนดเวลาสำหรับคำตอบของอเล็กซ์สำหรับความแตกต่าง 0.2 เรา)
Mike Boers

9

_ คือสิ่งเดียวกันกับ x อย่างไรก็ตามมันเป็นสำนวนไพ ธ อนที่ใช้เพื่อระบุตัวระบุที่คุณไม่ต้องการใช้ ในไพ ธ อนตัวระบุเหล่านี้จะไม่จดจำหรือจัดสรรพื้นที่เช่นตัวแปรทำในภาษาอื่น มันง่ายที่จะลืม พวกเขาเป็นเพียงชื่อที่ชี้ไปที่วัตถุในกรณีนี้เป็นจำนวนเต็มในการวนซ้ำแต่ละครั้ง


8

ฉันพบคำตอบต่าง ๆ ที่สวยงามจริงๆ (โดยเฉพาะของ Alex Martelli) แต่ฉันต้องการหาปริมาณการแสดงด้วยตัวเองดังนั้นฉันจึงทำสคริปต์ต่อไปนี้

from itertools import repeat
N = 10000000

def payload(a):
    pass

def standard(N):
    for x in range(N):
        payload(None)

def underscore(N):
    for _ in range(N):
        payload(None)

def loopiter(N):
    for _ in repeat(None, N):
        payload(None)

def loopiter2(N):
    for _ in map(payload, repeat(None, N)):
        pass

if __name__ == '__main__':
    import timeit
    print("standard: ",timeit.timeit("standard({})".format(N),
        setup="from __main__ import standard", number=1))
    print("underscore: ",timeit.timeit("underscore({})".format(N),
        setup="from __main__ import underscore", number=1))
    print("loopiter: ",timeit.timeit("loopiter({})".format(N),
        setup="from __main__ import loopiter", number=1))
    print("loopiter2: ",timeit.timeit("loopiter2({})".format(N),
        setup="from __main__ import loopiter2", number=1))

ฉันยังได้โซลูชันทางเลือกที่สร้างขึ้นบน Martelli's และใช้map()ในการเรียกฟังก์ชัน payload ตกลงฉันโกงนิดหน่อยว่าฉันรับอิสระในการทำ payload ยอมรับพารามิเตอร์ที่ถูกทิ้ง: ฉันไม่รู้ว่ามีวิธีแก้ไขไหม อย่างไรก็ตามนี่คือผลลัพธ์:

standard:  0.8398549720004667
underscore:  0.8413165839992871
loopiter:  0.7110594899968419
loopiter2:  0.5891903560004721

ดังนั้นการใช้แผนที่จะให้ผลดีขึ้นประมาณ 30% จากมาตรฐานของลูปและเพิ่มขึ้น 19% จากมาร์ติล


4

สมมติว่าคุณกำหนดdo_somethingเป็นฟังก์ชันและคุณต้องการดำเนินการNครั้ง บางทีคุณสามารถลองต่อไปนี้:

todos = [do_something] * N  
for doit in todos:  
    doit()

45
แน่ใจ อย่าเพิ่งเรียกใช้ฟังก์ชั่นหนึ่งล้านครั้งเราจัดสรรรายการหนึ่งล้านรายการด้วย หากซีพียูทำงานอยู่หน่วยความจำจะไม่เครียดด้วยหรือไม่ คำตอบนั้นไม่สามารถระบุได้ว่า“ ไม่มีประโยชน์” อย่างแน่นอน (มันแสดงวิธีการทำงานที่แตกต่างออกไป) ดังนั้นฉันจึงไม่สามารถลงคะแนนได้ แต่ฉันไม่เห็นด้วยและฉันไม่เห็นด้วยอย่างสิ้นเชิง
tzot

1
มันไม่ใช่แค่รายการของการอ้างอิง N ถึงค่าฟังก์ชันเดียวกันใช่หรือไม่
Nick McCurdy

ค่อนข้างดีกว่าที่จะทำfn() for fn in itertools.repeat(do_something, N)และบันทึกอาร์เรย์ก่อนสร้าง ... นี่คือสำนวนที่ฉันต้องการ
F1Rumors

1
@tzot ทำไมเสียงยกตัวอย่าง บุคคลนี้ใช้ความพยายามในการเขียนคำตอบและตอนนี้อาจไม่ได้รับการสนับสนุนในอนาคต แม้ว่าจะมีผลกระทบด้านประสิทธิภาพ แต่ก็เป็นตัวเลือกที่ใช้งานได้และโดยเฉพาะอย่างยิ่งหาก N มีขนาดเล็กประสิทธิภาพ / หน่วยความจำก็ไม่สำคัญ
davidscolgan

ฉันประหลาดใจอยู่เสมอว่าประสิทธิภาพที่นักพัฒนาของ Python หลงใหลนั้นคืออะไร :) แม้ว่าฉันจะยอมรับว่ามันไม่เป็นสำนวนและบางคนที่เพิ่งเริ่มอ่าน Python อาจไม่เข้าใจว่าเกิดอะไรขึ้นอย่างชัดเจนเหมือนกับการใช้ iterator
Asfand Qazi

1

ง่าย ๆ ในขณะที่วนรอบ

while times > 0:
    do_something()
    times -= 1

คุณมีตัวแปรอยู่แล้ว ทำไมไม่ใช้มัน?


1
ความคิดเดียวของฉันคือมันเป็นโค้ด 3 บรรทัดเทียบกับหนึ่ง (?)
AJP

2
@AJP - มากขึ้นเช่น 4 บรรทัดเทียบกับ 2 บรรทัด
ArtOfWarfare

เพิ่มการเปรียบเทียบ (ครั้ง> 0) และการลดลง (
คูณ

@ F1Rumors ยังไม่ได้วัด แต่ฉันจะแปลกใจถ้าคอมไพเลอร์ JIT เช่น PyPy ควรสร้างโค้ดที่ช้าลงสำหรับวงที่เรียบง่ายในขณะนั้น
Philipp Claßen
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.