简体   繁体   English

将argurments传递给python中的装饰器

[英]passing argurments to a decorator in python

I am using retry function from the package retrying . 我正在使用重试包中的试功能。 I want to pass the arguments of the retry decorator from a function and I am not sure how to achieve that. 我想从函数传递retry装饰器的参数,我不知道如何实现。

@retry # (wait_exponential_multiplier=x,wait_exponential_max=y)
def post(url, json, exponential_multiplier, exponential_max):
    ...
    return(abc)

I want to pass the arguments of retry when calling the post() . 我想在调用post()时传递retry的参数。 I know when the function is compiled, the resulting function object is passed to the decorator so I am not sure if this is possible- or if I should may be approach it differently. 我知道在编译function时,生成的function对象被传递给decorator所以我不确定这是否可行 - 或者我是否应该采用不同的方法。

Generally speaking there no good ways to achieve this. 一般来说,没有很好的方法来实现这一目标。 You surely can write code like this: 你当然可以写这样的代码:

def post(url, json):
    ...
    return(abc)

...
decorated_func = retry(wait_exponential_max=1)(post)
a = decorated_func(url, json)

and it will work. 它会起作用。 But it looks rather ugly and will construct decorated object for every call ("regular" decorators are executed once in import time). 但它看起来相当丑陋并将为每次调用构造装饰对象(“常规”装饰器在导入时执行一次)。

If decorator itself is not very complex - you can use this approach in some more user-friendly manner: 如果装饰器本身不是很复杂 - 您可以以一种更加用户友好的方式使用此方法:

def _post(url, json):
    return(abc)

def post(url, json, wait_exponential_max=None, **kwargs):
    return retry(wait_exponential_max=wait_exponential_max, **kwargs)(_post)(url, json)

If you just want to use the library as is, then you cannot really use the decorator like this. 如果你只是想按原样使用库,那么就不能像这样使用装饰器了。 It's arguments are constant from when it is invoked (excepting messing about with mutable arguments). 它的参数在被调用时是不变的(除了用可变参数搞乱)。 Instead, you could always invoke the decorator before calling the function each time. 相反,您可以在每次调用函数之前始终调用装饰器。 This allows you to change the retrying arguments as and when you need to. 这允许您在需要时更改重试参数。

eg. 例如。

def post(url, json):
    ...

rety(post, wait_exponential_multiplier=...)(url=..., json=...)

But at that point, you might as well just skip the decorator altogether, and use what the decorator is using. 但在那时,您可能只是完全跳过装饰器,并使用装饰器正在使用的东西。

from retrying import Retrying

def post(url, json):
    ...

Retrying(wait_exponential_multiplier=...).call(post, url=..., json=...)

Either of these ways allow you to keep the post function pure and abstracted away from the concept of retrying (making it easier to call post when you don't want retrying behaviour). 这些方法中的任何一种都允许您保持post函数的纯粹性和抽象性,使其远离重试的概念(当您不希望重试行为时更容易调用post )。

You could also write a convenience function that wrapper that fills in defaults for your program. 您还可以编写一个便利函数,该函数包含填充程序默认值的包装器。 eg. 例如。

def retrier(wait_exponential_multiplier=2, **kwargs):
    return Retrying(wait_exponential_multiplier=wait_exponential_multiplier, **kwargs)

retrier(wait_exponential_max=10).call(post, url=..., json=...)
retrier(wait_exponential_multiplier=3, wait_exponential_max=10).call(post, url=..., json=...)

You have to create a new decorator which pass its own arguments down to the decorated function and transforms the function using the retry decorator: 你必须创建一个新的装饰器,它将自己的参数传递给装饰函数,并使用retry装饰器转换函数:

def retry_that_pass_down_arguments(**decorator_arguments):
    def internal_decorator(f):
        def decorated_function(*args, **kwargs):
            # Add the decorator key-word arguments to key-word arguments of the decorated function
            kwargs.update(decorator_arguments) 
            return retry(**decorator_arguments)(f)(*args, **kwargs) 
        return decorated_function
    return internal_decorator

Then you can just do: 然后你可以这样做:

@retry_that_pass_down_arguments(wait_exponential_multiplier=x, wait_exponential_max=y)
def post(url, json, exponential_multiplier=None, exponential_max=None):
    ...
    return(abc)

This is a complement to Jundiaius's answers to show that you can even use the inspect module to correctly handle the signature of the decorated function: 这是对Jundiaius答案的补充,表明您甚至可以使用inspect模块正确处理装饰函数的签名:

def deco_and_pass(deco, **kwparams):
    """Decorates a function with a decorator and parameter.
The parameters are passed to the decorator and forwarded to the function
The function must be prepared to receive those parameters, but they will
be removed from the signature of the decorated function."""
    def outer(f):
        sig = inspect.signature(f)       # remove parameters from the function signature
        params = collections.OrderedDict(sig.parameters)
        for k in kwparams:
            del params[k]
        def inner(*args, **kwargs):      # define the decorated function
            kwargs.update(kwparams)      # add the parameters
            # and call the function through the parameterized decorator
            return deco(**kwparams)(f)(*args, **kwargs)
        inner.__signature__ = inspect.signature(f).replace(
            parameters = params.values())
        inner.__doc__ = f.__doc__        # update doc and signature
        return inner
    return outer

Example usage: 用法示例:

@deco_and_pass(retry,wait_exponential_multiplier=x,wait_exponential_max=y)
def post(url, json, exponential_multiplier, exponential_max):
    ...
    return(abc)
...
post(url, json)

The signature of the decorated function is only def post(url, json) 装饰函数的签名只是def post(url, json)

Limits: the above code only accepts and passes keyword arguments for the decorator 限制:上面的代码只接受并传递装饰器的关键字参数

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM