2009-03-02 10 views
7

Soy un gran admirador de Python's for...else syntax - es sorprendente la frecuencia con la que es aplicable y con qué eficacia puede simplificar el código.Usando para ... else en generadores Python

Sin embargo, no he encontrado una buena manera de utilizarlo en un generador, por ejemplo:

def iterate(i): 
    for value in i: 
     yield value 
    else: 
     print 'i is empty' 

En el ejemplo anterior, me gustaría que la declaración print para ser ejecutado sólo si i esta vacio. Sin embargo, como else solo respeta break y return, siempre se ejecuta, independientemente de la longitud de i.

Si es imposible usar for...else de esta manera, ¿cuál es el mejor enfoque para que la declaración print se ejecute únicamente cuando no se produce nada?

Respuesta

11

Estás rompiendo la definición de un generador, que debe lanzar una excepción cuando StopIteration iteración es completa (el cual es manejado automáticamente por una sentencia return en una función de generador)

Así:

def iterate(i): 
    for value in i: 
     yield value 
    return 

mejor dejar que el código de llamada manejar el caso de un iterador vacío:

count = 0 
for value in iterate(range([])): 
    print value 
    count += 1 
else: 
    if count == 0: 
     print "list was empty" 

podría ser una forma más limpia de hacer lo anterior, sino que debe funciona bien, y no cae en ninguna de las trampas comunes de "tratar un iterador como una lista" a continuación.

+3

El retorno está implícito al final de un generador. No es necesario incluirlo. – recursive

+0

Estaba pensando eso, pero pensé que lo dejaría explícito aquí. – Triptych

+2

+1: la "impresión i está vacía" es el problema de otra persona, no el del generador. –

-2

¿Qué hay de simple if-else?

def iterate(i): 
    if len(i) == 0: print 'i is empty' 
    else: 
     for value in i: 
      yield value 
+0

El argumento podría no tener un len definido, entonces esto es problemático. – Kiv

+0

Mismo problema que la respuesta recursiva, más o menos. – Triptych

5

Hay varias maneras de hacerlo. Se podría utilizar siempre el Iterator directamente:

def iterate(i): 
    try: 
     i_iter = iter(i) 
     next = i_iter.next() 
    except StopIteration: 
     print 'i is empty' 
     return 

    while True: 
     yield next 
     next = i_iter.next() 

Pero si usted sabe más acerca de lo que puede esperar del argumento i, puede ser más concisa:

def iterate(i): 
    if i: # or if len(i) == 0 
     for next in i: 
      yield next 
    else: 
     print 'i is empty' 
     raise StopIteration() 
0

Si es imposible use for ... else de esta manera, ¿cuál es el mejor enfoque para que la declaración de impresión solo se ejecute cuando no se produce nada?

máximo que puedo pensar:

 

>>> empty = True 
>>> for i in [1,2]: 
...  empty = False 
... if empty: 
...  print 'empty' 
... 
>>> 
>>> 
>>> empty = True 
>>> for i in []: 
...  empty = False 
... if empty: 
... print 'empty' 
... 
empty 
>>> 
 
3

Resumiendo algunas de las respuestas anteriores, podría ser resuelto de esta manera:

def iterate(i): 
    empty = True 
    for value in i: 
     yield value 
     empty = False 

    if empty: 
     print "empty" 

por lo que realmente no hay "otra cosa "cláusula involucrada.

3

Como observa, for..else solo detecta un break. Por lo tanto, solo es aplicable cuando busca algo y luego detiene.

No es aplicable a su propósito no porque sea un generador, pero porque quiere procesar todos los elementos, sin detener (porque quiere cederlos todos, pero ese no es el punto).

Así generador o no, realmente necesita un booleano, como en la solución de Ber.

Cuestiones relacionadas