4

Hi I need to have a function that would take a function and return a function that will run that argument function eg. 1000 times and each time evaluate it's argument. I have something like this:

def runner(f):
    def inner(*args):
        for i in xrange(1000):
            f(*args)
    return inner

But it seems that invocation like this:runner(f)(random.randint(1,UPPER_BOUND)) runs f 1000 times with the same argument. How to do it correctly?

k_wisniewski
  • 2,439
  • 3
  • 24
  • 31

3 Answers3

7

The problem you're encountering is that random.randint(1,UPPER_BOUND) is being evaluated once at the time the inner() function returned by runner() is called. What you need is to delay the evaluation until later.

You could try something like this:

>>> def runner(f, callable):
...   def inner():
...     for i in xrange(1000):
...       f(*callable())
...   return inner
... 
>>> runner(f, lambda: (random.randint(1, 1000),))()
603
385
321
etc.

Note that callable is called each time the original function f is called. Also note that callable must return a sequence type, like a tuple or list.

Edit: if you need to pass other arguments to f you can do it with something like this:

>>> def runner(f, callable):
...   def inner(*args, **kwds):
...     for i in xrange(1000):
...       pos = list(callable())
...       pos.extend(args)
...       f(*pos, **kwds)
...   return inner
... 
>>> def f(a, b, c, d = 3):
...   print a, b, c, d
... 
>>> runner(f, lambda: (random.randint(1,1000),))(3, 5, d = 7)
771 3 5 7
907 3 5 7
265 3 5 7
srgerg
  • 18,719
  • 4
  • 57
  • 39
1

You'll need to move your calculation of the random.randint into the function defintion:

For example, something like this should get you started, the @ is decorator syntax which you can read up on here if you aren't familiar with it. Shamelessly stealing the hello example from the other post:

import random

UPPER_BOUND = 1000

def runner(fn):
    def wrapped():
        for i in range(0,10):
            stuff = random.randint(1,UPPER_BOUND)
            print(str(stuff) + ': ' + fn())
    return wrapped

@runner
def hello():
    return 'hello world'


if __name__=='__main__':
    hello()

Edit: Also see here to understand why your random.randint gets executed only once (at definition time), which is why your function gets the same argument every time.

Community
  • 1
  • 1
Yuushi
  • 25,132
  • 7
  • 63
  • 81
1

You have to put the random.randit call inside the loop:

def runner(function):
    def inner(callable, args=()):
        for i in xrange(1000):
            function(callable(*args))
    return inner

And you can call the runner:

runner(f)(random.randint, args=(1, UPPER_BOND))

This seemed to me what you were trying to do, (and don't involves ugly lambdas).

Rik Poggi
  • 28,332
  • 6
  • 65
  • 82