Usando para ... else em geradores de Python
Pergunta
Eu sou um grande fã de Python da para ... else sintaxe - é surpreendente como muitas vezes é o caso, e como efetivamente ele pode simplificar o código
.No entanto, eu não descobri uma boa maneira de usá-lo em um gerador, por exemplo:
def iterate(i):
for value in i:
yield value
else:
print 'i is empty'
No exemplo acima, eu gostaria a declaração print
a ser executado somente se i
está vazio. No entanto, como else
apenas aspectos break
e return
, é sempre executado, independentemente da duração da i
.
Se é impossível usar for...else
desta forma, qual é a melhor abordagem para isso para que a declaração print
só é executado quando nada é produzido?
Solução
Você está quebrando a definição de um gerador, que deve lançar uma exceção StopIteration quando iteração é completo (que é automaticamente manipulado por uma instrução de retorno em uma função de gerador)
Assim:
def iterate(i):
for value in i:
yield value
return
Melhor para deixar o punho código chamando o caso de um iterador vazio:
count = 0
for value in iterate(range([])):
print value
count += 1
else:
if count == 0:
print "list was empty"
Pode ser uma maneira mais limpa de fazer o acima, mas que deve funcionar bem, e não se enquadra em qualquer um dos comum 'tratar um iterador como uma lista de' armadilhas abaixo.
Outras dicas
Existem algumas maneiras de fazer isso. Você pode sempre usar o Iterator
diretamente:
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()
Mas se você sabe mais sobre o que esperar do i
argumento, você pode ser mais conciso:
def iterate(i):
if i: # or if len(i) == 0
for next in i:
yield next
else:
print 'i is empty'
raise StopIteration()
Resumindo algumas das respostas anteriores, poderia ser resolvido assim:
def iterate(i):
empty = True
for value in i:
yield value
empty = False
if empty:
print "empty"
então não há realmente nenhuma cláusula "else" envolvida.
Como você notar, for..else
só detecta um break
. Portanto, é aplicável apenas quando você olha para alguma coisa e, em seguida, parada .
Não é aplicável ao seu propósito não porque é um gerador, mas porque você quer processar todos os elementos, sem parar (porque você quer produzir todos eles, mas esse não é o ponto).
Assim gerador ou não, você realmente precisa de um boolean, como na solução de Ber.
Se é impossível usar para ... else, desta forma, o que é a melhor abordagem para isso para que a declaração de impressão só é executado quando nada é produzido?
máximo que posso 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
>>>
E a simples if-else?
def iterate(i):
if len(i) == 0: print 'i is empty'
else:
for value in i:
yield value