簡體   English   中英

將argurments傳遞給python中的裝飾器

[英]passing argurments to a decorator in python

我正在使用重試包中的試功能。 我想從函數傳遞retry裝飾器的參數,我不知道如何實現。

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

我想在調用post()時傳遞retry的參數。 我知道在編譯function時,生成的function對象被傳遞給decorator所以我不確定這是否可行 - 或者我是否應該采用不同的方法。

一般來說,沒有很好的方法來實現這一目標。 你當然可以寫這樣的代碼:

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

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

它會起作用。 但它看起來相當丑陋並將為每次調用構造裝飾對象(“常規”裝飾器在導入時執行一次)。

如果裝飾器本身不是很復雜 - 您可以以一種更加用戶友好的方式使用此方法:

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)

如果你只是想按原樣使用庫,那么就不能像這樣使用裝飾器了。 它的參數在被調用時是不變的(除了用可變參數搞亂)。 相反,您可以在每次調用函數之前始終調用裝飾器。 這允許您在需要時更改重試參數。

例如。

def post(url, json):
    ...

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

但在那時,您可能只是完全跳過裝飾器,並使用裝飾器正在使用的東西。

from retrying import Retrying

def post(url, json):
    ...

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

這些方法中的任何一種都允許您保持post函數的純粹性和抽象性,使其遠離重試的概念(當您不希望重試行為時更容易調用post )。

您還可以編寫一個便利函數,該函數包含填充程序默認值的包裝器。 例如。

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=...)

你必須創建一個新的裝飾器,它將自己的參數傳遞給裝飾函數,並使用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

然后你可以這樣做:

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

這是對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

用法示例:

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

裝飾函數的簽名只是def post(url, json)

限制:上面的代碼只接受並傳遞裝飾器的關鍵字參數

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM