views:

60

answers:

3

I read a question earlier asking if there was a times method in Python, that would allow a function to be called n times in a row.

Everyone suggested for _ in range(n): foo() but I wanted to try and code a different solution using a function decorator.

Here's what I have:

def times(self, n, *args, **kwargs):
    for _ in range(n):
        self.__call__(*args, **kwargs)

import new
def repeatable(func):
    func.times = new.instancemethod(times, func, func.__class__)

@repeatable
def threeArgs(one, two, three):
    print one, two, three

threeArgs.times(7, "one", two="rawr", three="foo")

When I run the program, I get the following exception:

Traceback (most recent call last):
  File "", line 244, in run_nodebug
  File "C:\py\repeatable.py", line 24, in 
    threeArgs.times(7, "one", two="rawr", three="foo")
AttributeError: 'NoneType' object has no attribute 'times'

So I suppose the decorator didn't work? How can I fix this?

+3  A: 

Your decorator should return the function object:

def repeatable(func):
    func.times = new.instancemethod(times, func, func.__class__)
    return func

Now it returns nothing, so you actually change threeArgs in a None

This is because this:

@decorator
def func(...):
    ...

is more or less the same as:

def func(...):
    ....
func = decorator(func)
KillianDS
awesome, I guess I should have figured... oh well, thanks for your help
Carson Myers
+1  A: 

You're missing a return func statement at the end of your repeatable decorator.

Alex Martelli
A: 

Have you considered not adding it to specific functions and instead allowing its use with any function?

def times(n, func, *args, **kwds):
  return [func(*args, **kwds) for _ in xrange(n)]

(I'm returning a list of return values, but you could write it to ignore them, similar to the for-loop you have in the question.)

Then where you would, with your version, use:

threeArgs.times(7, "one", two="rawr", three="foo")

You instead use:

times(7, threeArgs, "one", two="rawr", three="foo")
Roger Pate