วิธีที่สะอาดที่สุดในการรับรายการสุดท้ายจาก Python iterator


110

วิธีที่ดีที่สุดในการรับรายการสุดท้ายจากตัววนซ้ำใน Python 2.6 คืออะไร? ตัวอย่างเช่นพูดว่า

my_iter = iter(range(5))

เป็นระยะเวลาที่สั้นรหัส / วิธีที่สะอาดในการได้รับอะไร4จากmy_iter?

ฉันทำได้ แต่ดูเหมือนไม่ค่อยมีประสิทธิภาพ:

[x for x in my_iter][-1]

4
ผู้ทำซ้ำสมมติว่าคุณต้องการวนซ้ำผ่านองค์ประกอบและไม่เข้าถึงองค์ประกอบสุดท้ายจริงๆ อะไรทำให้คุณหยุดเพียงแค่ใช้ช่วง (5) [- 1]
Frank

7
@ แฟรงค์ - ฉันคิดว่าตัววนซ้ำจริงนั้นซับซ้อนกว่าและ / หรืออยู่ไกลออกไปและ / หรือควบคุมยากกว่าiter(range(5))
Chris Lutz

3
@ แฟรงค์: ความจริงที่ว่ามันเป็นฟังก์ชั่นเครื่องกำเนิดไฟฟ้าที่ซับซ้อนกว่ามากที่จ่ายตัววนซ้ำ ฉันเพิ่งสร้างตัวอย่างนี้ขึ้นมาเพื่อให้ง่ายและชัดเจนว่าเกิดอะไรขึ้น
ปีเตอร์

4
หากคุณต้องการรายการสุดท้ายของตัววนซ้ำมีโอกาสมากที่คุณจะทำอะไรผิดพลาด แต่คำตอบก็คือไม่มีวิธีใดที่สะอาดกว่าการวนซ้ำผ่านตัววนซ้ำ เนื่องจากตัวทำซ้ำไม่มีขนาดและในความเป็นจริงอาจไม่สิ้นสุดเลยและอาจไม่มีรายการสุดท้าย (หมายถึงรหัสของคุณจะทำงานตลอดไปแน่นอน) คำถามที่ค้างคาคือทำไมคุณถึงต้องการรายการสุดท้ายของตัววนซ้ำ?
Lennart Regebro

3
@ ปีเตอร์: โปรดอัปเดตคำถามของคุณ อย่าเพิ่มความคิดเห็นจำนวนมากในคำถามที่คุณเป็นเจ้าของ โปรดอัปเดตคำถามและลบความคิดเห็น
ล็อต

คำตอบ:


100
item = defaultvalue
for item in my_iter:
    pass

4
ทำไมตัวยึดตำแหน่ง "defaultvalue"? ทำไมไม่None? นี่คือสิ่งที่แน่นอนNoneสำหรับ คุณกำลังแนะนำว่าค่าเริ่มต้นเฉพาะฟังก์ชันบางอย่างอาจถูกต้องหรือไม่? หาก iterator ไม่จริงย้ำแล้วออกจากวงของค่าอื่น ๆที่มีความหมายกว่าบางส่วนเข้าใจผิดเริ่มต้นฟังก์ชั่นที่เฉพาะเจาะจง
ล็อต

46
ค่าเริ่มต้นเป็นเพียงตัวยึดสำหรับตัวอย่างของฉัน หากคุณต้องการใช้Noneเป็นค่าเริ่มต้นนั่นคือทางเลือกของคุณ ไม่มีไม่ใช่ค่าเริ่มต้นที่เหมาะสมที่สุดเสมอไปและอาจไม่ได้อยู่นอกวง โดยส่วนตัวแล้วฉันมักจะใช้ 'defaultvalue = object ()' เพื่อให้แน่ใจว่าเป็นค่าที่ไม่ซ้ำใครจริงๆ ฉันแค่ระบุว่าตัวเลือกของค่าเริ่มต้นอยู่นอกขอบเขตของตัวอย่างนี้
Thomas Wouters

28
@ S.Lott: บางทีอาจเป็นประโยชน์ในการแยกแยะความแตกต่างระหว่างตัววนซ้ำว่างและตัววนซ้ำที่มีNoneค่าสุดท้าย
John La Rooy

8
มีข้อผิดพลาดในการออกแบบในตัวทำซ้ำทั้งหมดของคอนเทนเนอร์ในตัวทั้งหมด? ครั้งแรกที่ฉันเคยได้ยิน :)
Thomas Wouters

7
แม้ว่านี่อาจเป็นวิธีแก้ปัญหาที่เร็วกว่า แต่ก็ขึ้นอยู่กับตัวแปรที่รั่วไหลใน for-loops (คุณสมบัติสำหรับบางคนข้อบกพร่องสำหรับคนอื่น ๆ - อาจเป็น FP-guys ที่น่าตกใจ) อย่างไรก็ตาม Guido กล่าวว่าสิ่งนี้จะใช้ได้ผลเสมอดังนั้นจึงปลอดภัยที่จะใช้
tokland

68

ใช้dequeขนาด 1.

from collections import deque

#aa is an interator
aa = iter('apple')

dd = deque(aa, maxlen=1)
last_element = dd.pop()

6
นี่เป็นวิธีที่เร็วที่สุดในการทำให้ซีเควนซ์ยาวหมดเร็วที่สุดแม้ว่าจะมีความลื่นเร็วกว่าสำหรับลูปก็ตาม
Sven Marnach

11
+1 เพื่อความถูกต้องในทางเทคนิค แต่ผู้อ่านควรมีคำเตือนของ Python ตามปกติคือ "คุณจำเป็นต้องปรับให้เหมาะสมจริงหรือไม่" "นี่เป็นเรื่องที่ชัดเจนน้อยกว่าซึ่งไม่ใช่ Pythonic" และ "ความเร็วที่เร็วขึ้นขึ้นอยู่กับการนำไปใช้งานซึ่ง อาจมีการเปลี่ยนแปลง "
leewz

1
มันเป็นความทรงจำหมู
Eelco Hoogendoorn

6
@EelcoHoogendoorn ทำไมถึงเป็น memory-hog ได้ถึงแม้จะมี maxlen ถึง 1?
Chris Wesseling

1
จากการแก้ปัญหาทั้งหมดที่นำเสนอที่นี่เพื่อให้ห่างไกลฉันพบนี้เป็นที่เร็วที่สุดและมากที่สุดหน่วยความจำที่มีประสิทธิภาพอย่างใดอย่างหนึ่ง
Markus Strauss

67

หากคุณใช้ Python 3.x:

*_, last = iterator # for a better understanding check PEP 448
print(last)

หากคุณใช้ python 2.7:

last = next(iterator)
for last in iterator:
    continue
print last


หมายเหตุด้านข้าง:

โดยปกติแล้วโซลูชันที่นำเสนอข้างต้นเป็นสิ่งที่คุณต้องการสำหรับกรณีปกติ แต่ถ้าคุณกำลังจัดการกับข้อมูลจำนวนมากการใช้dequeขนาด 1 ( แหล่งที่มา ) จะมีประสิทธิภาพมากกว่า

from collections import deque

#aa is an interator
aa = iter('apple')

dd = deque(aa, maxlen=1)
last_element = dd.pop()

1
@virtualxtc: ขีดล่างเป็นเพียงตัวระบุ ดาวข้างหน้าพูดว่า "ขยายรายการ" ก็จะยิ่งอ่านได้มากขึ้น*lst, last = some_iterableเท่านั้น
pepr

4
@virtualxtc nope _เป็นตัวแปรพิเศษใน python และใช้เพื่อเก็บค่าสุดท้ายหรือเพื่อบอกว่าฉันไม่สนใจเกี่ยวกับค่าที่สามารถทำความสะอาดได้
DhiaTN

1
โซลูชัน Python 3 นั้นใช้หน่วยความจำไม่ได้
Markus Strauss

3
@DhiaTN ใช่คุณพูดถูกจริงๆ อันที่จริงฉันชอบสำนวน Python 3 ที่คุณแสดงมาก ฉันแค่อยากจะบอกให้ชัดเจนว่ามันใช้ไม่ได้กับ "ข้อมูลขนาดใหญ่" ฉันใช้ collection.deque เพื่อสิ่งนั้นซึ่งเร็วและหน่วยความจำมีประสิทธิภาพ (ดูวิธีแก้ปัญหาจาก martin23487234)
Markus Strauss

1
ตัวอย่าง py3.5 + นี้ควรอยู่ใน PEP 448 Wonderful
EliadL


27

ง่ายเหมือน:

max(enumerate(the_iter))[1]

8
โอ้นี่มันฉลาด ไม่ได้มีประสิทธิภาพมากที่สุดหรืออ่านได้ แต่ฉลาด
timgeb

6
ดังนั้นเพียงแค่คิดออกมาดัง ๆ ... สิ่งนี้ได้ผลเพราะenumerateผลตอบแทน(index, value)เช่น: (0, val0), (1, val1), (2, val2)... จากนั้นโดยค่าเริ่มต้นmaxเมื่อได้รับรายการสิ่งที่เพิ่มขึ้นให้เปรียบเทียบกับค่าแรกของทูเปิลเท่านั้นเว้นแต่ค่าสองค่าแรกจะเท่ากันซึ่งไม่เคยอยู่ที่นี่ เนื่องจากเป็นตัวแทนของดัชนี แล้วห้อยต่อท้ายเป็นเพราะผลตอบแทนสูงสุดทั้งหมด (IDX ค่า) tuple valueในขณะที่เรากำลังสนใจเฉพาะใน ความคิดที่น่าสนใจ
Taylor Edmiston

21

นี่ไม่น่าจะเร็วกว่าช่องว่างสำหรับลูปเนื่องจากแลมด้า แต่บางทีมันอาจจะให้ความคิดกับคนอื่น

reduce(lambda x,y:y,my_iter)

ถ้า iter ว่างเปล่า TypeError จะเพิ่มขึ้น


IMHO นี่เป็นแนวคิดที่ตรงไปตรงมาที่สุด แทนที่จะเพิ่มTypeErrorสำหรับ iterable ว่างคุณยังไม่สามารถใส่ค่าเริ่มต้นผ่านทางค่าเริ่มต้นของเช่นreduce() last = lambda iterable, default=None: reduce(lambda _, x: x, iterable, default)
egnha

9

มีสิ่งนี้

list( the_iter )[-1]

หากความยาวของการวนซ้ำเป็นเรื่องที่ยิ่งใหญ่ - นานจนทำให้รายการเป็นจริงจะทำให้หน่วยความจำหมดไป - คุณต้องคิดใหม่ในการออกแบบจริงๆ


1
นี่เป็นวิธีแก้ปัญหาที่ตรงไปตรงมาที่สุด
laike9m

2
ใช้ทูเพิลดีกว่าอย่างอ่อนโยน
Christopher Smith

9
ไม่เห็นด้วยอย่างยิ่งกับประโยคสุดท้าย การทำงานกับชุดข้อมูลขนาดใหญ่มาก (ซึ่งอาจเกินขอบเขตหน่วยความจำหากโหลดพร้อมกันทั้งหมด) เป็นเหตุผลหลักที่ต้องใช้ตัววนซ้ำแทนรายการ
Paul

@Paul: บางฟังก์ชันจะส่งคืนตัววนซ้ำเท่านั้น นี่เป็นวิธีที่สั้นและอ่านง่ายในกรณีนั้น (สำหรับรายการที่ไม่ใช่มหากาพย์)
บริการ

นั่นเป็นวิธีที่มีประสิทธิภาพน้อยที่สุดที่เราควรหลีกเลี่ยงเนื่องจากเป็นนิสัยที่ไม่ดีที่ไม่ดี อีกวิธีหนึ่งคือการใช้ sort (ลำดับ) [- 1] เพื่อให้ได้องค์ประกอบสูงสุดของลำดับ โปรดอย่าใช้วัสดุที่ไม่ดีเหล่านี้หากคุณต้องการเป็นวิศวกรซอฟต์แวร์
Maksym Ganenko

5

ฉันจะใช้ reversedยกเว้นว่าจะใช้ลำดับแทนการวนซ้ำซึ่งดูเหมือนจะเป็นไปตามอำเภอใจ

ไม่ว่าคุณจะทำอย่างไรคุณจะต้องเรียกใช้ตัวทำซ้ำทั้งหมด อย่างมีประสิทธิภาพสูงสุดหากคุณไม่ต้องการตัววนซ้ำอีกคุณสามารถทิ้งค่าทั้งหมดได้:

for last in my_iter:
    pass
# last is now the last item

ฉันคิดว่านี่เป็นวิธีแก้ปัญหาที่ไม่เหมาะสม


4
ย้อนกลับ () ไม่ใช้ตัววนซ้ำเพียงแค่ลำดับ
Thomas Wouters

3
มันไม่ได้เป็นไปตามอำเภอใจ วิธีเดียวที่จะย้อนกลับตัววนซ้ำคือการวนซ้ำไปจนสุดในขณะที่เก็บรายการทั้งหมดไว้ในหน่วยความจำ ผมคุณต้องสร้างลำดับจากมันก่อนจึงจะย้อนกลับได้ ซึ่งแน่นอนว่ามันเอาชนะจุดประสงค์ของตัววนซ้ำในตอนแรกและยังหมายความว่าคุณใช้หน่วยความจำไปมากในทันทีโดยไม่มีเหตุผลชัดเจน ในความเป็นจริงมันตรงกันข้ามกับพล :)
Lennart Regebro

@ เลนนาร์ท - เมื่อฉันพูดตามอำเภอใจฉันหมายถึงน่ารำคาญ ฉันกำลังจดจ่อทักษะทางภาษาบนกระดาษซึ่งจะครบกำหนดในเวลาไม่กี่ชั่วโมงในตอนเช้า
Chris Lutz

3
พอใช้. แม้ว่า IMO จะน่ารำคาญกว่าหากยอมรับตัวทำซ้ำเพราะการใช้งานเกือบทุกอย่างจะเป็นความคิดที่ไม่ดี (tm) :)
Lennart Regebro

3

Toolzห้องสมุดให้เป็นโซลูชั่นที่ดี:

from toolz.itertoolz import last
last(values)

แต่การเพิ่มการพึ่งพาที่ไม่ใช่คอร์อาจไม่คุ้มค่าสำหรับการใช้งานในกรณีนี้เท่านั้น


1

ดูรหัสนี้สำหรับสิ่งที่คล้ายกัน:

http://excamera.com/sphinx/article-islast.html

คุณอาจใช้เพื่อรับรายการสุดท้ายด้วย:

[(last, e) for (last, e) in islast(the_iter) if last]

1
โปรดใส่รหัสสำหรับislastคำตอบของคุณ (ดูmeta.stackexchange.com/questions/8231/… )
Cristian Ciupitu


0

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

ตัวอย่างที่สร้างขึ้น

>>> seq = list(range(10))
>>> last_even = next(_ for _ in reversed(seq) if _ % 2 == 0)
>>> last_even
8

0

อีกทางเลือกหนึ่งสำหรับการวนซ้ำแบบไม่มีที่สิ้นสุดคุณสามารถใช้ได้:

from itertools import islice 
last = list(islice(iterator(), 1000))[-1] # where 1000 is number of samples 

ฉันคิดว่ามันจะช้าลงdequeแต่มันเร็วและเร็วกว่านั้นสำหรับวิธีการวนซ้ำ (อย่างใด)


-6

คำถามไม่ถูกต้องและสามารถนำไปสู่คำตอบที่ซับซ้อนและไม่มีประสิทธิภาพเท่านั้น ในการรับตัวทำซ้ำแน่นอนว่าคุณต้องเริ่มจากสิ่งที่ทำซ้ำได้ซึ่งในกรณีส่วนใหญ่จะเสนอวิธีการเข้าถึงองค์ประกอบสุดท้ายที่ตรงกว่า

เมื่อคุณสร้างตัววนซ้ำจากการทำซ้ำคุณจะติดขัดในการดำเนินการผ่านองค์ประกอบต่างๆเพราะนั่นคือสิ่งเดียวที่สามารถทำซ้ำได้

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


5
แล้วคุณจะได้บรรทัดสุดท้ายของไฟล์ได้อย่างไร?
Brice M. Dempsey

@ BriceM.Dempsey วิธีที่ดีที่สุดไม่ใช่การทำซ้ำทั้งไฟล์ (อาจใหญ่มาก) แต่ไปที่ขนาดไฟล์ลบ 100 อ่าน 100 ไบต์สุดท้ายสแกนหาบรรทัดใหม่ในไฟล์หากไม่มีให้ไป ย้อนกลับอีก 100 ไบต์ ฯลฯ คุณยังสามารถเพิ่มขนาด step-back ได้ขึ้นอยู่กับสถานการณ์ของคุณ การอ่าน gazillion บรรทัดเป็นวิธีแก้ปัญหาที่ไม่เหมาะสม
Alfe
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.