Python'daki yineleyiciler neden bir istisna yaratıyor?


48

İşte Java'da yineleyiciler için sözdizimi (C # ile benzer bir sözdizimi):

Iterator it = sequence.iterator();

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

Bu mantıklı. İşte Python'da eşdeğer sözdizimi:

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

İstisnaların yalnızca istisnai durumlarda kullanılması gerektiğini düşündüm.

Python neden yinelemeyi durdurmak için istisnalar kullanıyor?


Yanıtlar:


50

Açık bir şekilde bir try-hariç bloğu yazmadan bu ifadeyi yazmanın çok Pythonic bir yolu var 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)

Neden tanıtıldığını ve yineleyicilerin ardındaki mantığı öğrenmek istiyorsanız ilgili PEP 234 255'i okuyabilirsiniz StopIteration.

Python'daki genel bir prensip, bir şeyi yapmanın bir yoluna (bkz. import this) Ve tercihen, pitonik yöntemin karşıladığı güzel, açık, okunabilir ve basittir. Eşdeğer kodunuz yalnızca python yineleyicilere hasNextüye işlevi vermediğinden gereklidir; İnsanların doğrudan yineleyiciler arasında dolaşmasını tercih etmek (ve sadece okumayı denemek ve bir istisna yakalamak için başka bir şey yapmanız gerekiyorsa).

StopIterationBir yineleyicinin sonunda bir istisnanın bu otomatik olarak yakalanması bir anlam ifade eder ve EOFErrordosyanın sonundan önce okursanız , yükseltilenlerin bir benzeridir .


6
"Pythonic" yolu kesinlikle "dizideki değer için:" yerine "iter (dizideki) değer" gibi görünüyor: Güncelleme?
Yam Marcovic

15
@Yam: Katılıyorum. Mevcut bir diziyi alıp sadece bir for döngüsü uygulamak için onu bir yineleyiciye dönüştürmek için pitonik değildir; sekansı, daha önce bir iterable, yani bir dönüştürülmesi lista listiteratoranlamsızdır. Ben sadece nasıl olması gerektiği, aynı şekilde bir yineleyici üzerinde döngü yapmalısınız herhangi iterable üzerinde döngü (açıklamak, NullUserException en başlangıç noktasını takip etmek ilk satırı tuttu list, set, str, tuple, dict, file, generator, vb.) it = itertools.combinations("ABCDE", 2)Anlamlı bir yinelemenin daha iyi bir örneğini almak gibi bir şey yapabilirdim .
dr jimbob

1
it = iter(sequence)Gerek yok.
Caridorc

2
@Caridorc - Yorumları okuduysan, anladın mı? Sorunun başlangıç ​​noktasını takip etmek gerekli değil ve yapıldı (açıkça sordukları yer iterators) ve iteraçıkça bir iterator(try type([])( list) vs type(iter([]))( listiterator)) oluşturmanız gerekiyor.
dr jimbob

//, @drjimbob, bu sorunun ikinci yorumunda mükemmel bir noktaya değindiniz. Yinelemelerin gelişmiş özellikleri konusunda biraz yeniyim ve eğer yorumları okumamış olsaydım yakalayamazdım. Sorunun kendisinin cevabın ilk kısmı olarak "Python Yolu" olarak nasıl değiştirilebileceğini anlayabilseydik, kendi kendini eğiten kötü ruhların çoğuna fayda sağlayacağını düşünüyorum .
Nathan Basanese

28

Python'un bir yinelemeyi durdurmak için İstisna kullanmasının nedeni, PEP 234'te belgelenmiştir :

Yinelemenin sonunu gösteren bir istisnanın çok pahalı olmadığı sorgulandı. StopIteration istisnası için çeşitli alternatifler önerilmiştir: özel bir değer Sona işaret etmek için End, yineleyicinin bitip bitmediğini test etmek için bir işlev sonu (), IndexError istisnasını yeniden kullanmak bile.

  • Özel bir değer, bir dizide bu özel değeri içeriyorsa, bu dizinin üzerindeki bir döngünün herhangi bir uyarı olmadan erken biteceği sorunu vardır. Boş sonlandırılmış C dizeleriyle ilgili deneyimler bize bunun neden olabileceği problemleri öğretmediyse, bir Python inceleme aracının tüm yerleşik adların bir listesini yinelemesini, özel Son değerin bir yerleşik olduğunu varsayarak düşünmesini bekleyin. adına!

  • Bir end () işlevini çağırmak, yineleme başına iki çağrı gerektirir. İki arama, bir aramadan çok daha pahalıdır ve bir istisna testidir. Özellikle döngü için zaman açısından kritik olan bir istisna için çok ucuza test yapabilirsiniz.

  • IndexError'ı yeniden kullanmak karışıklığa neden olabilir, çünkü döngüyü erken bitirerek maskelenecek olan gerçek bir hata olabilir.

Not: Bir dizi üzerinde döngü yapmanın deyimsel python yolu şöyledir:

for value in sequence:
    print (value)

20

Felsefede bir fark var. Pythonic tasarım felsefesi EAFP'dir :

Bağışlanmak için izin almaktan daha kolay. Bu yaygın Python kodlama stili, geçerli anahtarların veya niteliklerin varlığını varsayar ve varsayım yanlış olduğunu kanıtlarsa istisnalar yakalar. Bu temiz ve hızlı stil birçok tryve exceptifadelerin varlığı ile karakterizedir . Bu teknik , C gibi diğer birçok dilde ortak olan LBYL stili ile çelişir .


7

Sadece Java uygulamasının bir hasNext()metoda sahip olması, böylece bir işlemi gerçekleştirmeden önce boş bir yineleyiciyi kontrol edebilirsiniz next(). Bir next()Java yineleyicisini hiçbir öğe bırakmadan aradığınızda , a NoSuchElementExceptionatılır .

Yani etkili bir şekilde, bir deneyin yapabilirsin .. Python'daki bir deneyin gibi. Ve evet, önceki bir cevaba göre, Pythonic dünyasında felsefe çok önemlidir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.