Hexic
Hexic

Reputation: 17

Python iterating inside function arguments

I know I’ve seen (perhaps exclusively in other languages) where you can use for loops in function arguments. I forget what it was called, but in an attempt to make my code smaller I want to try it. For those of you who don't know what I'm talking about, it goes something like this:

math.sum(for i in range(5)) # Just an example; code will probably not work

Or something like that? I'm not sure how it works yet, but I intend to learn. I know there is a name for this sort of thing, but I've forgotten what it is. Could anyone give me some pointers, or am I insane and this doesn't exist in python?

Upvotes: 0

Views: 59

Answers (2)

abarnert
abarnert

Reputation: 366133

A "for loop as an expression" is usually called a "comprehension", at least in Haskell, Python, and other languages inspired by them.

Read List Comprehensions in the tutorial for an introduction to the idea. There are also set comprehensions and dict comprehensions, which are pretty obvious once you get list comprehensions.

Then there are generator expressions, which are a bit trickier—but a lot cooler. You're not going to understand those until you first read Iterators, and then Generators, and then Generator Expressions is the very next section.

It still probably won't be clear why generator expressions are cool, but David Beazley explains that masterfully.


To translate your code to real code, all you need is:

math.sum(i for i in range(5))

However, what you're asking for is "all of the elements of range(5), which you can do a lot more easily like this:

math.sum(range(5))

Why? Because a range is already an iterable object.* If it weren't, you couldn't use it in a for loop in the first place, by definition. Unless you have either some expression to perform on each element, or an if clause to filter the loop, or multiple for clauses for nested looping, comprehensions don't buy you anything. So, here's some more useful examples:

math.sum(i*i for i in range(5))
math.sum(i for i in range(5) if i%3 != 0)
math.sum(j for i in range(5) for j in range(i))

* Technically speaking, you're asking for an iterator over all of the elements in range(5), not just any iterable over them. For a for loop it doesn't matter, but if you need something that you can call next on manually, have it remember its current position, etc., it does. In that case, what you want is iter(range(5)).


The fact that your comprehension happens to be a function argument is almost completely irrelevant here. You can use them anywhere you can use an expression:

squares_to_5 = (i*i for i in range(5)) # often useful
for square in (i*i for i in range(5)): # silly, but legal

However, notice that generator expressions need to be put inside parentheses. In the special case where a generator expression is the only argument to a function, so it's already in parentheses, you can leave the extra parentheses off.

Upvotes: 2

Wayne Werner
Wayne Werner

Reputation: 51907

You're thinking of list comprehensions and generator expressions.

This would work in Python with only a slight modification:

sum(i for i in range(5))

This is the seminal work on generators: http://www.dabeaz.com/generators/

Technically speaking they are completely unrelated to the fact that you're using them as function arguments:

x = (i for i in range(5))
evens = [i for i in range(100) if i % 2 == 0]
even_squares = [i**2 for i in evens]

Upvotes: 1

Related Questions