Usando para & # 8230; else en generadores de Python
Pregunta
Soy un gran fan de de Python para ... la sintaxis de else : es sorprendente la frecuencia con la que se aplica y la eficacia con la que puede simplificar el código.
Sin embargo, no he descubierto una buena forma de usarlo 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
se ejecute solo si i
está vacío. Sin embargo, como else
solo respeta break
y return
, siempre se ejecuta, independientemente de la longitud de i
.
Si es imposible usar para ... else
de esta manera, ¿cuál es el mejor enfoque para que la declaración print
solo se ejecute cuando no se obtiene nada?
Solución
Está rompiendo la definición de un generador, que debería lanzar una excepción StopIteration cuando se complete la iteración (que se maneja automáticamente mediante una declaración de retorno en una función de generador)
Entonces:
def iterate(i):
for value in i:
yield value
return
Es mejor dejar que el código de llamada maneje 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, pero debería funcionar bien, y no caer en ninguna de las trampas comunes de "tratar un iterador como una lista" a continuación.
Otros consejos
Hay un par de maneras de hacer esto. Siempre se puede usar 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 sabe más sobre qué esperar del argumento i
, puede ser más conciso:
def iterate(i):
if i: # or if len(i) == 0
for next in i:
yield next
else:
print 'i is empty'
raise StopIteration()
Resumiendo algunas de las respuestas anteriores, podría resolverse así:
def iterate(i):
empty = True
for value in i:
yield value
empty = False
if empty:
print "empty"
así que realmente no hay " else " cláusula involucrada.
Como usted observa, for..else
solo detecta un break
. Por lo tanto, solo se aplica cuando buscas algo y luego para .
No es aplicable a su propósito, no porque sea un generador, sino porque desea procesar todos los elementos, sin detenerse (porque desea cederlos a todos, pero ese no es el punto).
Generador o no, realmente necesitas un booleano, como en la solución de Ber.
Si es imposible de usar para ... de otra forma, ¿cuál es el mejor enfoque para que la declaración de impresión solo se ejecute cuando no se obtiene 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
>>>
¿Qué pasa con simple si-else?
def iterate(i):
if len(i) == 0: print 'i is empty'
else:
for value in i:
yield value