Mengapa iterator di Python memunculkan eksepsi?

48

Berikut sintaks untuk iterator di Jawa (sintaksis yang agak mirip di C #):

Iterator it = sequence.iterator();

while (it.hasNext()) {
    System.out.println(it.next());
}

Itu masuk akal. Berikut sintaks yang setara dalam Python:

it = iter(sequence)
while True:
    try:
        value = it.next() 
    except StopIteration:
        break
    print(value)

Saya pikir Pengecualian seharusnya hanya digunakan dalam, yah, keadaan luar biasa.

Mengapa Python menggunakan pengecualian untuk menghentikan iterasi?

NullUserException
sumber

Jawaban:

50

Ada cara yang sangat Pythonic untuk menulis ekspresi itu tanpa secara eksplisit menulis blok coba-kecuali untuk StopIteration:

# some_iterable is some collection that can be iterated over
# e.g., a list, sequence, dict, set, itertools.combination(...)

for value in some_iterable:
    print(value)

Anda dapat membaca PEPs 234 255 yang relevan jika Anda ingin tahu lebih banyak di balik mengapa StopIterationdiperkenalkan dan logika di balik iterator.

Prinsip umum dalam python adalah memiliki satu cara untuk melakukan sesuatu (lihat import this), dan lebih disukai yang indah, eksplisit, mudah dibaca, dan sederhana, yang memenuhi metode pythonic. Kode setara Anda hanya diperlukan karena python tidak memberikan hasNextfungsi anggota kepada iterator ; lebih suka orang untuk hanya mengulangi iterator secara langsung (dan jika Anda perlu melakukan sesuatu yang lain untuk hanya mencoba membacanya dan menangkap pengecualian).

Penangkapan otomatis StopIterationpengecualian di akhir iterator ini masuk akal dan merupakan analog dari yang EOFErrordibangkitkan jika Anda membaca melewati akhir file.

dr jimbob
sumber
6
Cara "Pythonic" tampaknya lebih seperti "untuk nilai dalam urutan:" daripada "untuk nilai dalam iter (urutan):" .. Perbarui posting?
Yam Marcovic
15
@Yam: Saya setuju. Ini bukan pythonic untuk mengambil urutan yang ada dan mengubahnya menjadi iterator hanya untuk menerapkan for loop; urutannya sudah iterable, jadi konversi a listke listiteratortidak ada gunanya. Aku terus baris pertama hanya untuk mengikuti titik NullUserException ini mulai, untuk menjelaskan bagaimana Anda harus loop melalui iterator, yang merupakan cara yang sama Anda harus loop atas setiap iterable ( list, set, str, tuple, dict, file, generator, dll). Saya bisa melakukan sesuatu seperti it = itertools.combinations("ABCDE", 2)untuk mendapatkan contoh yang lebih baik dari iterator yang berarti.
dr jimbob
1
it = iter(sequence)tidak dibutuhkan.
Caridorc
2
@Caridorc - Jika Anda membaca komentar, poin Anda akan ditanggapi. Itu tidak diperlukan dan dilakukan untuk mengikuti titik awal dari pertanyaan (di mana mereka secara eksplisit bertanya tentang iterators) dan Anda memang perlu itersecara eksplisit menghasilkan iterator(coba type([])( list) vs type(iter([]))( listiterator)).
dr Jimbob
//, @drjimbob, Anda mendapatkan poin yang sangat baik di komentar kedua untuk pertanyaan ini. Saya agak baru dengan fitur-fitur canggih dari iterables, dan saya tidak akan menangkap itu jika saya belum membaca komentar. Saya pikir itu akan menguntungkan banyak dari kita jiwa-jiwa miskin yang belajar sendiri jika kita dapat melihat poin itu tentang bagaimana pertanyaan itu sendiri dapat diubah menjadi "The Python Way" sebagai bagian pertama, jawaban utama.
Nathan Basanese
28

Alasan mengapa python menggunakan Exception untuk menghentikan iterasi didokumentasikan dalam PEP 234 :

Telah dipertanyakan apakah pengecualian untuk menandai akhir iterasi tidak terlalu mahal. Beberapa alternatif untuk pengecualian StopIteration telah diusulkan: nilai khusus Ujung untuk menandai akhir, fungsi akhir () untuk menguji apakah iterator selesai, bahkan menggunakan kembali pengecualian IndexError.

  • Nilai khusus memiliki masalah bahwa jika suatu urutan pernah mengandung nilai khusus itu, loop di atas urutan itu akan berakhir sebelum waktunya tanpa peringatan. Jika pengalaman dengan string C yang diakhiri-nol belum mengajari kami masalah yang dapat ditimbulkannya, bayangkan masalah alat introspeksi Python akan mengulangi daftar semua nama bawaan, dengan asumsi bahwa nilai Akhir khusus adalah bawaan. dalam nama!

  • Memanggil fungsi end () akan membutuhkan dua panggilan per iterasi. Dua panggilan jauh lebih mahal daripada satu panggilan plus tes untuk pengecualian. Terutama waktu-kritis untuk loop dapat menguji dengan sangat murah untuk pengecualian.

  • Menggunakan kembali IndexError dapat menyebabkan kebingungan karena ini bisa menjadi kesalahan asli, yang akan ditutup dengan mengakhiri loop sebelum waktunya.

Catatan: cara python idiomatik untuk mengulang urutan adalah seperti ini:

for value in sequence:
    print (value)
lesmana
sumber
20

Ini perbedaan filosofi. Filosofi desain Pythonic adalah EAFP :

Lebih mudah untuk meminta maaf daripada izin. Gaya pengkodean Python yang umum ini mengasumsikan adanya kunci atau atribut yang valid dan menangkap pengecualian jika asumsi terbukti salah. Gaya bersih dan cepat ini ditandai dengan kehadiran banyak orang trydan exceptpernyataan. Teknik ini kontras dengan gaya LBYL yang umum untuk banyak bahasa lain seperti ...

Charles E. Grant
sumber
7

Hanya saja implementasi Java memiliki hasNext()metode sehingga Anda dapat memeriksa iterator kosong sebelum Anda melakukan a next(). Ketika Anda memanggil next()iterator Java tanpa elemen yang tersisa, a NoSuchElementExceptiondilemparkan .

Jadi secara efektif, Anda dapat melakukan try..catch di Jawa seperti try..except dengan Python. Dan ya, sesuai jawaban sebelumnya, filsafat sangat penting di dunia Pythonic.

yati sagade
sumber