2009-03-02 11 views
7

Jestem wielkim fanem Pythona for...else syntax - zaskakuje, jak często jest to możliwe i jak skutecznie może uprościć kod.Używanie dla ... jeszcze w generatorach Pythona

Jednak nie zorientowali się piękny sposób na wykorzystanie go w generatorze, na przykład:

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

w powyższym przykładzie, chciałbym oświadczenie print być wykonywane tylko wtedy, gdy i jest pusty. Jednak jako else tylko respektuje break i return, jest zawsze wykonywane, niezależnie od długości i.

Jeśli w ten sposób niemożliwe jest użycie for...else, jakie jest najlepsze podejście do tego, aby instrukcja print była wykonywana tylko wtedy, gdy nic nie zostanie wygenerowane?

Odpowiedz

11

Łamiesz definicji generator, który powinien rzucić wyjątek StopIteration gdy iteracja jest kompletna (który jest automatycznie obsługiwane przez instrukcji return w funkcji generatora)

sposób:

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

Najlepiej niech kod wywołujący obsłużyć przypadek pustym iterator:

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

może być czystszy sposób prowadzenia wyżej, ale które powinny działa dobrze i nie należy do typowych pułapek "traktujących tak jak listy", jak poniżej.

+3

powrót jest dorozumiany na końcu generatora. Nie trzeba go uwzględniać. – recursive

+0

Myślałem o tym, ale pomyślałem, że zostawię to tutaj wyraźnie. – Triptych

+2

+1: "print i is empty" to czyjś problem, a nie generator. –

-2

Co z prostym, jeśli nie?

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

Argument I może nie mieć zdefiniowanego len, więc jest to problematyczne. – Kiv

+0

Ten sam problem, co odpowiedź rekursywna, mniej więcej. – Triptych

5

Istnieje kilka sposobów na zrobienie tego. Zawsze możesz użyć Iterator bezpośrednio:

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() 

Ale jeśli wiesz więcej o tym, co można oczekiwać od argumentu i, możesz być bardziej zwięzły:

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

Jeśli jest to niemożliwe użyć dla ... else w ten sposób, jakie jest najlepsze podejście do tego, aby instrukcja print była wykonywana tylko wtedy, gdy nic nie jest przekazywane?

Maksymalna mogę myśleć:

 

>>> 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

podsumowując niektóre z wcześniejszych odpowiedzi, to może być rozwiązane tak:

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

    if empty: 
     print "empty" 

tak naprawdę nie ma „else "zaangażowana klauzula.

3

Jak widzisz, for..else wykrywa tylko break. Ta opcja ma zastosowanie tylko wtedy, gdy czegoś szukasz, a następnie stop.

Nie dotyczy to twojego celu, nie dlatego, że jest to generator, ale , ponieważ chcesz przetworzyć wszystkie elementy, bez zatrzymywania (ponieważ chcesz je wszystkie poddać, ale nie o to chodzi).

Generator, czy nie, naprawdę potrzebujesz boolean, jak w rozwiązaniu Ber'a.