views:

174

answers:

3

More and more features of Python move to be "lazy executable", like generator expressions and other kind of iterators. Sometimes, however, I see myself wanting to roll a one liner "for" loop, just to perform some action.

What would be the most pythonic thing to get the loop actually executed?

For example:

a = open("numbers.txt", "w")
(a.write ("%d " % i) for i in xrange(100))
a.close()

Not actuall code, but you see what I mean. If I use a list generator, instead, I have the side effect of creating a N-lenght list filled with "None"'s.

Currently what I do is to use the expression as the argument in a call to "any" or to "all". But I would like to find a way that would not depend on the result of the expression performed in the loop - both "any" and "all" can stop depending on the expression evaluated.

To be clear, these are ways to do it that I already know about, and each one has its drawbacks:

[a.write ("%d " % i) for i in xrange(100))]

any((a.write ("%d " % i) for i in xrange(100)))

for item in (a.write ("%d " % i) for i in xrange(100)): pass
+6  A: 

If I wanted to do this specific example, I'd write

for i in xrange(100): a.write('%d ' % i)

If I often needed to consume an iterator for its effect, I'd define

def for_effect(iterable):
    for _ in iterable:
        pass
Darius Bacon
I think the for loop looks better over 2 lines. We like Python's indenty spaces
gnibbler
Yeah, I'd usually write it that way, too, but the OP might not have known of the one-line form, and specifically asked for one.
Darius Bacon
+3  A: 

There are many accumulators which have the effect of consuming the whole iterable they're given, such as min or max -- but even they don't ignore entirely the results yielded in the process (min and max, for example, will raise an exception if some of the results are complex numbers). I don't think there's a built-in accumulator that does exactly what you want -- you'll have to write (and add to your personal stash of tiny utility function) a tiny utility function such as

def consume(iterable):
    for item in iterable: pass

The main reason, I guess, is that Python has a for statement and you're supposed to use it when it fits like a glove (i.e., for the cases you'd want consume for;-).

BTW, a.write returns None, which is falsish, so any will actually consume it (and a.writelines will do even better!). But I realize you were just giving that as an example;-).

Alex Martelli
Actually, there *are* ways to accomplish what is asked for without the shortcoming of `min/max` - in one line and without creating list of len(N). I found 3 (three) distinct ways after a good night of sleep - but in the words of [Fermat](http://en.wikipedia.org/wiki/Fermat's_Last_Theorem) - 'This margin is too narrow to contain [this truly marvelous proof]' :-)
Nas Banov
@Nas, there's obvious goofy ways such as `[x for x in iterable if False]` (makes an empty list) and the similar `any(x and False for x in iterable)` (doesn't make any list at all) and `all(x or True for x in iterable)` -- but (while they do fit just fine in this margin;-) they're all seriously cringe-worthy.
Alex Martelli
@Nas, well..feel free to use the main "page" instead of the margin then. :-)
jsbueno
@Alex: Ah, very well - you mention one of the ways i thought of. About the other two you show - i wonder, are we guaranteed that bool(x) works for every type? (i assume this is about equivalent to being able to use object with operators `and/or/not`) ? PS. and no, i did not say i found any *pretty* or *pythonic* way
Nas Banov
@jsbueno: Hmmm, i don't know - and got slapped again with votes down? Anyone throw a bounty bone? :)
Nas Banov
@Nas, a (weird) type _might_ throw an exception in `__len__` (etc), you're right. Plus, I'm not sure why I bothered coding them that way when the obvious simplification to `all(True for x in iterable)` etc avoids the risk _and_ is marginally faster;-). (It's even [[slightly]] less cringe-inducing -- or maybe that's the effect of the beer I just had at the company's usual TGIF weekly meeting;-).
Alex Martelli
+11  A: 

There is one obvious way to do it, and that is the way you should do it. There is no excuse for doing it a clever way.

a = open("numbers.txt", "w")
for i in xrange(100):
    a.write("%d " % i)
d.close()

Lazy execution gives you a serious benefit: It allows you to pass a sequence to another piece of code without having to hold the entire thing in memory. It is for the creation of efficient sequences as data types.

In this case, you do not want lazy execution. You want execution. You can just ... execute. With a for loop.

Jerub