views:

142

answers:

1

I'm a rookie hobbyist and I nest for loops when I write python, like so:

dict = {
    key1: {subkey/value1: value2} 
    ... 
    keyn: {subkeyn/valuen: valuen+1}
    }

for key in dict:
    for subkey/value in key:
       do it to it

I'm aware of a "next" keyword that would accomplish the same goal in one line (I asked a question about how to use it but I didn't quite understand it).

So to me, a nested for loop is much more readable. Why, then do people use "next"? I read somewhere that Python is a dynamically-typed and interpreted language and because + both concontinates strings and sums numbers, that it must check variable types for each loop iteration in order to know what the operators are, etc. Does using "next" prevent this in some way, speeding up the execution or is it just a matter of style/preference?

+7  A: 

next is precious to advance an iterator when necessary, without that advancement controlling an explicit for loop. For example, if you want "the first item in S that's greater than 100", next(x for x in S if x > 100) will give it to you, no muss, no fuss, no unneeded work (as everything terminates as soon as a suitable x is located) -- and you get an exception (StopIteration) if unexpectedly no x matches the condition. If a no-match is expected and you want None in that case, next((x for x in S if x > 100), None) will deliver that. For this specific purpose, it might be clearer to you if next was actually named first, but that would betray its much more general use.

Consider, for example, the task of merging multiple sequences (e.g., a union or intersection of sorted sequences -- say, sorted files, where the items are lines). Again, next is just what the doctor ordered, because none of the sequences can dominate over the others by controlling A "main for loop". So, assuming for simplicity no duplicates can exist (a condition that's not hard to relax if needed), you keep pairs (currentitem, itsfile) in a list controlled by heapq, and the merging becomes easy... but only thanks to the magic of next to advance the correct file once its item has been used, and that file only.

import heapq

def merge(*theopentextfiles):
    theheap = []
    for afile in theopentextfiles:
        theitem = next(afile, '')
        if theitem: theheap.append((theitem, afile))
    heapq.heapify(theheap)
    while theheap:
        theitem, afile = heapq.heappop(theheap)
        yielf theitem
        theitem = next(afile, '')
        if theitem: heapq.heappush(theheap, (theitem, afile))

Just try to do anything anywhere this elegant without next...!-)

One could go on for a long time, but the two use cases "advance an iterator by one place (without letting it control a whole for loop)" and "get just the first item from an iterator" account for most important uses of next.

Alex Martelli
+1 Nice answer!
AJ
@AJ, thank you!
Alex Martelli
+1 You did it again. I just realized that my useful any aka first could be defined (first non-False in any sequence or last value in sequence if all are False) could be defined also with next and iter.
Tony Veijalainen
@Tony, what does your "useful any aka first" return for an _empty_ sequence? Same as for `[False]`, `[None]`, or what else? BTW, I think `next(itertools.ifilter(None, seq), None)` may be more practical (returns `None` for _any_ sequence without any true item, including empty and non-empty ones... seems more uniform!).
Alex Martelli
I came up with same version, but thought my requirements would be allmost matched better with `next(itertools.ifilter(None, seq), type(seq)())`. Uniformity is important and I was thinking uniformity with shortcircuit `or` of Python á la Lisp `(OR ...)`. As Python has separate [], (), etc. the question of empty sequence needs consideration. Probably most Pythonic answer would be to leave the next to raise exception in that case (one parameter next). Also the normal function is so clean that benefit of next in this case is questionable as `for` does the job nicely in function.
Tony Veijalainen
Ok, thanks. Next seems easy to use now actually. I guess it was adding the ", None" that was throwing me.
aquateenfan
@aquateenfan, good point -- you only need the `, None` (or `, ''` as I have in an example above, and so forth) if you'd rather avoid the exception when the iterator's exhausted (sometimes such exhaustion is unexpected and best turned into an exception, sometimes it's fully expected and best handled by other means).
Alex Martelli