Perché gli iteratori in Python generano un'eccezione?

47

Ecco la sintassi per gli iteratori in Java (sintassi alquanto simile in C #):

Iterator it = sequence.iterator();

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

Il che ha un senso. Ecco la sintassi equivalente in Python:

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

Pensavo che le eccezioni dovessero essere utilizzate solo in, beh, circostanze eccezionali.

Perché Python usa le eccezioni per fermare l'iterazione?

    
posta NullUserException 05.10.2011 - 04:03
fonte

4 risposte

50

C'è un modo molto Python per scrivere quell'espressione senza scrivere esplicitamente un blocco try-except per 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

Puoi leggere i PEP pertinenti 234 255 se vuoi saperne di più sul perché è stato introdotto StopIteration e sulla logica degli iteratori.

Un principio generale in python è avere un modo per fare qualcosa (vedi import this ), e preferibilmente è bello, esplicito, leggibile e semplice, che il metodo pythonic soddisfa. Il tuo codice equivalente è necessario solo in quanto python non fornisce agli iteratori una funzione membro hasNext ; preferendo che gli utenti passino direttamente agli iteratori (e se devi fare qualcos'altro per provare a leggerlo e rilevare un'eccezione).

Questo rilevamento automatico di un'eccezione StopIteration alla fine di un iteratore ha senso ed è un analogo del EOFError generato se si legge una fine del file.

    
risposta data 05.10.2011 - 04:44
fonte
28

Il motivo per cui python utilizza un'eccezione per interrompere un'iterazione è documentato in PEP 234 :

It has been questioned whether an exception to signal the end of the iteration isn't too expensive. Several alternatives for the StopIteration exception have been proposed: a special value End to signal the end, a function end() to test whether the iterator is finished, even reusing the IndexError exception.

  • A special value has the problem that if a sequence ever contains that special value, a loop over that sequence will end prematurely without any warning. If the experience with null-terminated C strings hasn't taught us the problems this can cause, imagine the trouble a Python introspection tool would have iterating over a list of all built-in names, assuming that the special End value was a built-in name!

  • Calling an end() function would require two calls per iteration. Two calls is much more expensive than one call plus a test for an exception. Especially the time-critical for loop can test very cheaply for an exception.

  • Reusing IndexError can cause confusion because it can be a genuine error, which would be masked by ending the loop prematurely.

Nota: il modo idiomatic python per eseguire il loop su una sequenza è simile a questo:

for value in sequence:
    print (value)
    
risposta data 07.12.2012 - 08:59
fonte
20

È una differenza nella filosofia. La filosofia del design Pythonic è EAFP :

Easier to ask for forgiveness than permission. This common Python coding style assumes the existence of valid keys or attributes and catches exceptions if the assumption proves false. This clean and fast style is characterized by the presence of many try and except statements. The technique contrasts with the LBYL style common to many other languages such as C...

    
risposta data 05.10.2011 - 04:36
fonte
7

È solo che l'implementazione Java ha un metodo hasNext() in modo da poter controllare un iteratore vuoto prima di eseguire un next() . Quando chiami next() su un iteratore Java senza elementi, a NoSuchElementException viene generato .

Così efficacemente, puoi fare un tentativo..catch in Java come try..except in Python. E sì, come da una risposta precedente, la filosofia è molto importante nel mondo Pythoniano.

    
risposta data 05.10.2011 - 04:46
fonte

Leggi altre domande sui tag