Search code examples
pythondecorator

python decorator for class methods


I have a decorator to register some class methods. How can I get both self and run parameters correctly?

class Task(object):
    _tasks = []

    @staticmethod
    def register(name):
        def decorator(fn):
            @wraps(fn)
            def wrapper(self=None, run=True, *args, **kwargs):
                if not run:
                    task = defaultdict()
                    task['name'] = name
                    task['fn'] = getattr(self, fn.__name__, None)
                    task['obj'] = self
                    task['args'] = deepcopy(args)
                    task['kwargs'] = deepcopy(kwargs)
                    Task._tasks.append(task)
                else:
                    return fn(self, *args, **kwargs)
            return wrapper
        return decorator

class Test(object):
    def __init__(self, name):
        self.name = name

    @Task.register('foo')
    def foo(self, v1, v2):
        print 'running foo in object {} with arguments {} {}'.format(self.name, v1, v2)

    @Task.register('hello')
    def hello(self):
        print 'running hello in object {} '.format(self.name)

    def load(self):
        self.foo('1', '2', run=False)
        self.hello(run=False)

t1=Test('t1')
t1.load()

The following error was raised:

Traceback (most recent call last):

    TypeError: wrapper() got multiple values for keyword argument 'run'

Solution

  • Your problem has nothing to do with the decorator. Your code is actually doing the same as this:

    def foo(run=False, *args, **kwargs):
        print(run, args, kwargs)
    
    foo(1, 2, run=True)  # TypeError: foo() got multiple values for argument 'run'
    

    From your function's signature, python will try to set run=1, args = (2,) and then run into the TypeError.

    A fix - though not a very nice one - could be:

    def foo(*args, **kwargs):
        run = kwargs.pop('run', False)  # run defaults to False; remove from kwargs
        print(run, args, kwargs)