# [Tutor] Calling a function

Thu Jun 9 09:31:23 CEST 2005

```Quoting Kevin Reeder <reederk at comcast.net>:

> The second module is timings.py.
>
> import time, makezeros
>
> def do_timing(num_times, *funcs):
>  totals = {}
>  for func in funcs: totals[func] = 0.0
> 	for x in range(num_times):
> 	for func in funcs:
> 	 starttime = time.time()
> 	 apply(func)
> 	 stoptime = time.time()
> 	 elapsed = stoptime-starttime
> 	 totals[func] = totals[func] + elapsed
> 	 for func in funcs:
> 	 print "Running %s %d times took %.3f seconds" %
> (func.__name__, num_times, totals[func])
>
> do_timing(100, (makezeros.lots_of_appends, makezeros.one_multiply))

Hi Kevin,

I have two comments.  First, a simple one:

Using "apply" is no longer necessary in python.  It is better to just call the
function directly.  (ie: replace 'apply(func)' with 'func()')

You have (possibly) slightly misunderstood the "star" notation.

Consider:

>>> def printArgs(*args):
...     print args
...
>>> printArgs(1, 2, 'foo', 'bar')
(1, 2, 'foo', 'bar')

Basically, all the arguments to printArgs get collected into a tuple, which gets
the name 'args' in this example.

Here's another example:

>>> def printArgs2(x, y, *args):
...     print 'x:', x
...     print 'y:', y
...     print 'remaining:', args
...
>>> printArgs2(1, 'foo', 3, 'hello world', 99)
x: 1
y: foo
remaining: (3, 'hello world', 99)

In this case, x and y consumed the first two arguments to printArgs2, and the
remainder went into the catch-all args.

Now, lets look at the relevant bits of your code:

----
def do_timing(num_times, *funcs):
...
----

This says: "Call the first argument 'num_times', and put any other arguments
into a tuple called 'funcs'."

You then do this:

----
do_timing(100, (makezeros.lots_of_appends, makezeros.one_multiply))
----

Here, you are calling do_timing with _two_ arguments.

First argument:   100
Second argument:  (makezeros.lots_of_appends, makezeros.one_multiply)

Your second argument is a tuple with two elements.  funcs in your function will
be set to a tuple containing all remaining arguments --- ie:
((makezeros.lots_of_appends, makezeros.one_multiply),)  (which is a tuple with
one element, whose element is a tuple with two elements).

You can correct this in two ways:
- Change the function definition to:
def do_timing(num_times, funcs):
Now, funcs is just an ordinary positional argument, and it will take whatever
you give it.  If you give it a tuple of functions, then you can iterate over it
the way you expect.

or:
- Change the function call to:
do_timing(100, makezeros.lots_of_appends, makezeros.one_multiply)
*funcs will consume the two function arguments, giving you the same result as
in the first option.

My personal preference would be for the first option, which I think is clearer..

(it better expresses the idea that you have written a function which will take a
list of functions and time them)

HTH!

--
John.
```