Python: shortcut for writing decorators which accept arguments?

后端 未结 5 542
闹比i
闹比i 2021-02-06 02:45

Does the Python standard library have a shortcut for writing decorators which accept arguments?

For example, if I want to write a decorator like with_timeout(timeo

相关标签:
5条回答
  • 2021-02-06 03:12

    I know you said it feels suboptimal but I still feel that using three nested models is the cleanest solution. The inner two functions are just the 'normal' way of defining a decorator for a function that takes arguments (see example in python's docs for @wraps). The outer one is really just a function that takes and argument and returns a decorator.

    def with_timeout(timeout):
        def decorator(f):
            @wraps(f)
            def wrapper(*args, **kwargs):
                with Timeout(timeout):
                    return f(*args, **kwargs)
            return wrapper
        return decorator
    
    0 讨论(0)
  • 2021-02-06 03:13

    First, we can define a little meta-decorator:

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

    That allows us to create decorators that accept arguments like so:

    @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
    

    Which can then be used normally:

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

    Adding functools.wraps decoration is left as an exercise :)

    0 讨论(0)
  • 2021-02-06 03:28

    Another take, without using 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
    
    0 讨论(0)
  • 2021-02-06 03:30

    Based on Jakob's suggestion, I've implemented a small Decorator class, which I feel does a fairly decent job:

    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)
    
    0 讨论(0)
  • 2021-02-06 03:35

    I tend to write my decorators as classes to be honest

    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
    

    Its nothing if not slightly clearer

    0 讨论(0)
提交回复
热议问题