Python类方法的装饰器

6

我有一个装饰器来注册一些类方法。如何正确获取selfrun参数?

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()

出现了以下错误:

Traceback (most recent call last):

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

你的代码没有你所描述的错误。(假设 from functools import * - user202729
3个回答

7

你的问题与装饰器无关。实际上,你的代码做的与下面这段代码是一样的:

def foo(run=False, *args, **kwargs):
    print(run, args, kwargs)

foo(1, 2, run=True)  # TypeError: foo() got multiple values for argument 'run'

从您的函数签名中,Python 将尝试设置 run=1args=(2,),然后遇到 TypeError
一种解决方法 - 虽然不是很好 - 可能是:
def foo(*args, **kwargs):
    run = kwargs.pop('run', False)  # run defaults to False; remove from kwargs
    print(run, args, kwargs)

我正在做的是,我想将一个“run”参数传递给装饰器,告诉装饰器注册任务或仅执行它。但我不想在我的类方法中添加这个“run”参数,因为它在方法中没有任何作用。在这种情况下,我应该如何传递/处理“run”参数? - Mr.Dreamer
你可以将我所称的 foo 作为你的 wrapper 函数。修改其签名并从 kwargs 中获取 run。这实际上就是另一个答案详细建议的方式... - hiro protagonist
是的,这可以是一个解决方案。谢谢! - Mr.Dreamer

1

run参数来自于fun,因此尝试从函数的参数中获取它:

from collections import defaultdict
from copy import deepcopy
from functools import wraps

class Task(object):
    _tasks = []

    @staticmethod
    def register(name):
        def decorator(fn):
            @wraps(fn)
            def wrapper(self=None, *args, **kwargs):
                run = kwargs.pop('run', True)
                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

0

Python3似乎对参数有更好的处理方式,但不知道如何在Python2中实现:

from functools import wraps
def optional_debug(func):
    @wraps(func)
    def wrapper(*args, debug=False, **kwargs):
        if debug:
            print('Calling', func.__name__)
        return func(*args, **kwargs)
    return wrapper

@optional_debug
def spam(a,b,c):
    print(a,b,c)
spam(1,2,3) # 1,2,3
spam(1,2,3, debug=True) # Calling spam # 1 2 3

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接