Por que os iteradores no Python levantam uma exceção?

47

Aqui está a sintaxe para iteradores em Java (uma sintaxe semelhante em C #):

Iterator it = sequence.iterator();

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

O que faz sentido. Aqui está a sintaxe equivalente em Python:

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

Eu achava que Exceções deveriam ser usadas apenas em circunstâncias excepcionais.

Por que o Python usa exceções para interromper a iteração?

    
por NullUserException 05.10.2011 / 04:03
fonte

4 respostas

50

Existe uma maneira muito Python de escrever essa expressão sem escrever explicitamente um bloco try-except para um 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

Você pode ler os PEPs relevantes 234 255 se você quiser saber mais por que StopIteration foi introduzido e a lógica por trás dos iteradores.

Um princípio geral em python é ter uma maneira de fazer algo (ver import this ) e, de preferência, seu belo, explícito, legível e simples, que o método pythonic satisfaz. Seu código equivalente é necessário apenas porque o python não fornece aos iteradores uma função hasNext member; preferindo que as pessoas simplesmente percorram os iteradores diretamente (e se você precisar fazer outra coisa apenas para tentar lê-lo e capturar uma exceção).

Essa captura automática de uma exceção StopIteration no final de um iterador faz sentido e é um análogo do EOFError gerado se você ler além do final do arquivo.

    
por 05.10.2011 / 04:44
fonte
28

A razão pela qual o python usa uma exceção para interromper uma iteração está documentada em 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.

Observação: a maneira idiomática do Python de fazer um loop sobre uma sequência é assim:

for value in sequence:
    print (value)
    
por 07.12.2012 / 08:59
fonte
20

É uma diferença na filosofia. A filosofia de design do 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...

    
por 05.10.2011 / 04:36
fonte
7

É só que a implementação Java tem um método hasNext() para que você possa verificar um iterador vazio antes de executar um next() . Quando você chama next() em um iterador Java sem nenhum elemento, um NoSuchElementException é lançado .

Assim, você pode fazer uma tentativa..catch em Java como o try..except em Python. E sim, de acordo com uma resposta anterior, a filosofia é muito importante no mundo pitônico.

    
por 05.10.2011 / 04:46
fonte