Python:编写接受参数的装饰器的快捷方式?

时间:2012-05-16 01:14:38

标签: python decorator

Python标准库是否有编写接受参数的装饰器的快捷方式?

例如,如果我想写一个像with_timeout(timeout)

这样的装饰器
@with_timeout(10.0)
def cook_eggs(eggs):
    while not eggs.are_done():
        eggs.cook()

我必须写一些类似的东西:

def with_timeout(timeout):
    _func = [None]
    def with_timeout_helper(*args, **kwargs):
        with Timeout(timeout):
            return _func[0](*args, **kwargs)
    def with_timeout_return(f):
        return functools.wraps(f)(with_timeout_helper)
    return with_timeout_return

但这非常冗长。是否有一个快捷方式使得接受参数的装饰器更容易编写?

注意:我意识到也可以使用三个嵌套函数来实现带参数的装饰器......但这感觉也有点不太理想。

例如,可能类似于@decorator_with_arguments函数:

@decorator_with_arguments
def timeout(f, timeout):
    @functools.wraps(f)
    def timeout_helper(*args, **kwargs):
        with Timeout(timeout):
            return f(*args, **kwargs)
    return timeout_helper

5 个答案:

答案 0 :(得分:7)

我倾向于把装饰师写成班级,说实话

class TestWithArgs(object):
    def __init__(self, *deco_args, **deco_kwargs):
        self.deco_args = deco_args
        self.deco_kwargs = deco_kwargs
    def __call__(self, func):
        def _wrap(self, *args, **kwargs):
            print "Blah blah blah"
            return func(*args, **kwargs)
        return _wrap

如果不是更清楚的话,它什么都没有

答案 1 :(得分:4)

我知道你说它感觉不是很理想,但我仍然觉得使用三个嵌套模型是最干净的解决方案。内部的两个函数只是为接受参数的函数定义装饰器的“常规”方式(参见python的@wraps文档中的example)。外部实际上只是一个带有参数并返回装饰器的函数。

def with_timeout(timeout):
    def decorator(f):
        @wraps(f)
        def wrapper(*args, **kwargs):
            with Timeout(timeout):
                return f(*args, **kwargs)
        return wrapper
    return decorator

答案 2 :(得分:4)

根据雅各布的建议,我实施了一个小Decorator课程,我觉得这个课程相当不错:

class Decorator(object):
    def __call__(self, f):
        self.f = f
        return functools.wraps(f)(lambda *a, **kw: self.wrap(*a, **kw))

    def wrap(self, *args, **kwrags):
        raise NotImplemented("Subclasses of Decorator must implement 'wrap'")

class with_timeout(Decorator):
    def __init__(self, timeout):
        self.timeout = timeout

    def wrap(self, *args, **kwargs):
        with Timeout(timeout):
            return self.f(*args, **kwargs)

答案 3 :(得分:0)

首先,我们可以定义一个小的装饰器:

def decorator_with_arguments(wrapper):
    return lambda *args, **kwargs: lambda func: wrapper(func, *args, **kwargs)

这允许我们创建接受这样的参数的装饰器:

@decorator_with_arguments
def my_wrapper(func, *decorator_args, **decorator_kwargs):
    def wrapped(*call_args, **call_kwargs):
        print "from decorator:", decorator_args, decorator_kwargs
        func(*call_args, **call_kwargs)
    return wrapped

然后可以正常使用:

@my_wrapper(1, 2, 3)
def test(*args, **kwargs):
    print "passed directly:", args, kwargs

test(4, 5, 6)

添加functools.wraps装饰作为练习:)

答案 4 :(得分:0)

另一种观点,不使用lambdas:

def decorator_with_arguments(f):
    @functools.wraps(f)
    def with_arguments_helper(*args, **kwargs):
        def decorator(g):
            return f(g, *args, **kwargs)
        return decorator
    return with_arguments_helper