Cara terbersih untuk mendapatkan item terakhir dari iterator Python

110

Apa cara terbaik untuk mendapatkan item terakhir dari iterator dengan Python 2.6? Misalnya, katakanlah

my_iter = iter(range(5))

Apa terpendek-kode / cara terbersih mendapatkan 4dari my_iter?

Saya bisa melakukan ini, tetapi sepertinya tidak terlalu efisien:

[x for x in my_iter][-1]
Peter
sumber
4
Iterator berasumsi bahwa Anda ingin mengulang melalui elemen dan tidak benar-benar mengakses elemen terakhir. Apa yang menghentikan Anda dari sekadar menggunakan range (5) [- 1]?
Frank
7
@Frank - Saya berasumsi iterator sebenarnya lebih kompleks dan / atau lebih jauh dan / atau lebih sulit dikendalikan daripadaiter(range(5))
Chris Lutz
3
@Frank: fakta bahwa sebenarnya ini adalah fungsi generator yang jauh lebih rumit yang memasok iterator. Saya hanya membuat contoh ini sehingga sederhana dan jelas apa yang terjadi.
Peter
4
Jika Anda menginginkan item terakhir dari sebuah iterator, ada kemungkinan besar Anda melakukan kesalahan. Tetapi jawabannya adalah tidak ada cara yang lebih bersih untuk melakukan iterasi melalui iterator. Ini karena iterator tidak memiliki ukuran, dan pada kenyataannya, mungkin tidak akan pernah berakhir sama sekali, dan karena itu mungkin tidak memiliki item terakhir. (Artinya kode Anda akan berjalan selamanya, tentu saja). Jadi pertanyaan yang tersisa adalah: Mengapa Anda menginginkan item terakhir dari sebuah iterator?
Lennart Regebro
3
@Peter: Perbarui pertanyaan Anda. Jangan menambahkan banyak komentar ke pertanyaan Anda sendiri. Harap perbarui pertanyaan dan hapus komentar.
S. Lotot

Jawaban:

100
item = defaultvalue
for item in my_iter:
    pass
Thomas Wouters
sumber
4
Mengapa placeholder "defaultvalue"? Mengapa tidak None? Inilah tepatnya untuk apa None. Apakah Anda menyarankan bahwa beberapa nilai default khusus fungsi bisa jadi benar? Jika iterator tidak benar-benar melakukan iterasi, maka nilai out-of-band lebih bermakna daripada beberapa default khusus fungsi yang menyesatkan.
S. Lotot
46
Nilai default hanyalah placeholder untuk contoh saya. Jika Anda ingin menggunakan Nonesebagai nilai default, itu pilihan Anda. Tidak ada tidak selalu merupakan default yang paling masuk akal, dan bahkan mungkin tidak keluar jalur. Secara pribadi saya cenderung menggunakan 'defaultvalue = object ()' untuk memastikan itu adalah nilai yang benar-benar unik. Saya hanya menunjukkan bahwa pilihan default berada di luar cakupan contoh ini.
Thomas Wouters
28
@ S. Lott: mungkin berguna untuk membedakan perbedaan antara iterator kosong dan iterator yang memiliki Nonenilai akhirnya
John La Rooy
8
Ada kesalahan desain di semua iterator dari semua jenis wadah bawaan? Pertama kali saya mendengarnya :)
Thomas Wouters
7
Meskipun ini mungkin solusi yang lebih cepat, ini bergantung pada variabel yang bocor di for-loop (fitur untuk beberapa, bug untuk yang lain - mungkin FP-guys terkejut). Bagaimanapun, Guido mengatakan ini akan selalu bekerja dengan cara ini, jadi ini adalah konstruksi yang aman untuk digunakan.
tokland
68

Gunakan dequeukuran 1.

from collections import deque

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

dd = deque(aa, maxlen=1)
last_element = dd.pop()
martin23487234
sumber
6
Ini sebenarnya cara tercepat untuk menyelesaikan urutan panjang, meski hanya sedikit lebih cepat daripada putaran for.
Sven Marnach
11
+1 karena secara teknis benar, tetapi pembaca harus memiliki peringatan Python yang biasa, "Apakah Anda BENAR-BENAR perlu mengoptimalkan ini?", "Ini kurang eksplisit, yang bukan Pythonic", dan "Kecepatan yang lebih cepat tergantung pada implementasi, yang mana mungkin berubah. "
leewz
1
juga, ini adalah memori-babi
Eelco Hoogendoorn
6
@EelcoHoogendoorn Mengapa ini adalah memori-hog, bahkan dengan maxlen 1?
Chris Wesseling
1
Dari semua solusi yang disajikan sejauh ini, menurut saya ini adalah yang tercepat dan paling hemat memori .
Markus Strauss
66

Jika Anda menggunakan Python 3.x:

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

jika Anda menggunakan python 2.7:

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


Catatan Samping:

Biasanya, solusi yang disajikan di atas adalah yang Anda butuhkan untuk kasus biasa, tetapi jika Anda berurusan dengan data dalam jumlah besar, lebih efisien menggunakan dequeukuran 1. ( sumber )

from collections import deque

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

dd = deque(aa, maxlen=1)
last_element = dd.pop()
DhiaTN
sumber
1
@virtualxtc: Garis bawah hanyalah sebuah pengenal. Bintang di depan bertuliskan "perluas daftar". Akan lebih mudah dibaca *lst, last = some_iterable.
pepr
4
@virtualxtc nope _adalah variabel khusus dalam python dan digunakan baik untuk menyimpan nilai terakhir atau untuk mengatakan saya tidak peduli tentang nilainya jadi bisa dibersihkan.
DhiaTN
1
Solusi Python 3 itu tidak hemat memori.
Markus Strauss
3
@DhiaTN Ya, Anda benar sekali. Sebenarnya, saya suka idiom Python 3 yang sering Anda tunjukkan. Saya hanya ingin menjelaskan, bahwa ini tidak berfungsi untuk "data besar". Saya menggunakan collections.deque untuk itu, yang kebetulan cepat dan hemat memori (lihat solusi dari martin23487234).
Markus Strauss
1
Contoh py3.5 + ini harus dalam PEP 448. Bagus.
EliadL
33

Mungkin layak digunakan __reversed__jika tersedia

if hasattr(my_iter,'__reversed__'):
    last = next(reversed(my_iter))
else:
    for last in my_iter:
        pass
John La Rooy
sumber
27

Sesederhana:

max(enumerate(the_iter))[1]
Chema Cortes
sumber
8
Oh, ini pintar. Bukan yang paling efisien atau mudah dibaca, tapi pintar.
timgeb
6
Jadi hanya berpikir keras ... Ini berfungsi karena enumeratemengembalikan (index, value)seperti: (0, val0), (1, val1), (2, val2)... dan kemudian secara default maxketika diberi daftar tupel, membandingkan hanya dengan nilai pertama tupel, kecuali dua nilai pertama sama, yang tidak pernah ada di sini karena mewakili indeks. Kemudian subskrip tambahan adalah karena max mengembalikan seluruh tupel (idx, nilai) sedangkan kita hanya tertarik value. Ide yang menarik.
Taylor Edmiston
21

Ini tidak mungkin lebih cepat daripada loop for yang kosong karena lambda, tapi mungkin ini akan memberi orang lain ide

reduce(lambda x,y:y,my_iter)

Jika iter kosong, TypeError dimunculkan

John La Rooy
sumber
IMHO, ini adalah yang paling langsung, secara konseptual. Alih-alih menaikkan TypeErroriterable kosong, Anda juga bisa memberikan nilai default melalui nilai awal reduce(), misalnya last = lambda iterable, default=None: reduce(lambda _, x: x, iterable, default).
egnha
9

Ada ini

list( the_iter )[-1]

Jika panjang iterasi benar-benar epik - begitu lama sehingga mewujudkan daftar akan menghabiskan memori - maka Anda benar-benar perlu memikirkan ulang desainnya.

S. Lott
sumber
1
Ini adalah solusi paling mudah.
laike9m
2
Sedikit lebih baik menggunakan tupel.
Christopher Smith
9
Sangat tidak setuju dengan kalimat terakhir. Bekerja dengan set data yang sangat besar (yang dapat melebihi batas memori jika dimuat sekaligus) adalah alasan utama untuk menggunakan iterator, bukan daftar.
Paul
@ Paul: beberapa fungsi hanya mengembalikan iterator. Ini adalah cara singkat dan cukup mudah dibaca untuk melakukannya dalam kasus itu (untuk daftar non-epik).
serv-inc
Itulah cara yang paling tidak efisien yang harus dihindari sebagai kebiasaan buruk yang buruk. Cara lainnya adalah dengan menggunakan sort (urutan) [- 1] untuk mendapatkan elemen maksimum dari urutan tersebut. Harap jangan pernah menggunakan pola buruk ini jika Anda ingin menjadi insinyur perangkat lunak.
Maksym Ganenko
5

Saya akan menggunakan reversed, kecuali bahwa itu hanya membutuhkan urutan, bukan iterator, yang tampaknya agak sewenang-wenang.

Dengan cara apa pun Anda melakukannya, Anda harus menjalankan seluruh iterator. Pada efisiensi maksimum, jika Anda tidak membutuhkan iterator lagi, Anda dapat membuang semua nilai:

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

Saya pikir ini adalah solusi yang kurang optimal.

Chris Lutz
sumber
4
reversed () tidak membutuhkan iterator, hanya urutan.
Thomas Wouters
3
Itu sama sekali tidak sembarangan. Satu-satunya cara untuk membalikkan iterator adalah dengan mengulang sampai akhir, sambil menyimpan semua item dalam memori. I, e, Anda harus terlebih dahulu membuat urutan darinya, sebelum Anda dapat membalikkannya. Yang tentu saja mengalahkan tujuan iterator di tempat pertama, dan juga berarti Anda tiba-tiba menggunakan banyak memori tanpa alasan yang jelas. Jadi sebenarnya kebalikan dari yang sewenang-wenang. :)
Lennart Regebro
@ Lennart - Ketika saya mengatakan sewenang-wenang, maksud saya menyebalkan. Saya memfokuskan keterampilan bahasa saya pada makalah saya yang dijadwalkan beberapa jam lagi saat ini di pagi hari.
Chris Lutz
3
Cukup adil. Meskipun IMO akan lebih menjengkelkan jika menerima iterator, karena hampir semua penggunaan akan menjadi Ide Buruk (tm). :)
Lennart Regebro
3

The Toolz perpustakaan menyediakan solusi yang bagus:

from toolz.itertoolz import last
last(values)

Tetapi menambahkan ketergantungan non-inti mungkin tidak sepadan untuk digunakan hanya dalam kasus ini.

lumbric
sumber
0

Saya hanya akan menggunakan next(reversed(myiter))

thomas.mac
sumber
8
TypeError: argumen untuk dibalik () harus berurutan
Labo
0

Pertanyaannya adalah tentang mendapatkan elemen terakhir dari sebuah iterator, tetapi jika iterator Anda dibuat dengan menerapkan kondisi ke suatu urutan, maka pembalikan dapat digunakan untuk menemukan "pertama" dari urutan terbalik, hanya dengan melihat elemen yang dibutuhkan, dengan menerapkan mundur ke urutan itu sendiri.

Contoh yang dibuat-buat,

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

Sebagai alternatif untuk iterator tak terbatas, Anda dapat menggunakan:

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

Saya pikir itu akan lebih lambat, dequetetapi secepat dan sebenarnya lebih cepat daripada untuk metode loop (entah bagaimana)

qocu
sumber
-6

Pertanyaannya salah dan hanya dapat menghasilkan jawaban yang rumit dan tidak efisien. Untuk mendapatkan iterator, Anda tentu saja memulai dari sesuatu yang dapat diulang, yang dalam banyak kasus menawarkan cara yang lebih langsung untuk mengakses elemen terakhir.

Setelah Anda membuat iterator dari iterable, Anda akan terjebak dalam melewati elemen, karena itulah satu-satunya hal yang disediakan oleh iterable.

Jadi, cara yang paling efisien dan jelas bukanlah dengan membuat iterator pada awalnya, tetapi menggunakan metode akses asli dari iterable.

Ludwig
sumber
5
Jadi, bagaimana Anda akan mendapatkan baris terakhir dari sebuah file?
Brice M. Dempsey
@ BriceM.Dempsey Cara terbaik bukanlah dengan mengulang seluruh file (mungkin besar) tetapi dengan membuka ukuran file minus 100, membaca 100 byte terakhir, memindai baris baru di dalamnya, jika tidak ada, lanjutkan mundur 100 byte lagi, dll. Anda juga dapat meningkatkan ukuran langkah mundur, tergantung pada skenario Anda. Membaca baris trilyun jelas merupakan solusi yang tidak optimal.
Alfe