# frange() question

John J. Lee jjl at pobox.com
Thu Sep 20 20:55:47 CEST 2007

```George Trojan <george.trojan at noaa.gov> writes:

> A while ago I found somewhere the following implementation of frange():
>
> def frange(limit1, limit2 = None, increment = 1.):
>     """
>     Range function that accepts floats (and integers).
>     Usage:
>     frange(-2, 2, 0.1)
>     frange(10)
>     frange(10, increment = 0.5)
>     The returned value is an iterator.  Use list(frange) for a list.
>     """
>     if limit2 is None:
>         limit2, limit1 = limit1, 0.
>     else:
>         limit1 = float(limit1)
>     count = int(math.ceil(limit2 - limit1)/increment)
>     return (limit1 + n*increment for n in range(count))
>
> I am puzzled by the parentheses in the last line. Somehow they make
> frange to be a generator:
>>> print type(frange(1.0, increment=0.5))
> <type 'generator'>

Functions are never generators, senso stricto.  There are "generator
functions", which *return* (or yield) generators when you call them.
It's true sometimes people refer to generator functions as simply
"generators", but in examples like the above, it's useful to remember
that they are two different things.

In this case, frange isn't a generator function, because it doesn't
yield.  Instead, it returns the result of evaluating a generator
expression (a generator).  The generatator expression plays the same
role as a generator function -- calling a generator function gives you
a generator object; evaluating a generator expression gives you a
generator object.  There's nothing to stop you returning that
generator object, which makes this function behave just like a regular
generator function.

John

```