在 python 中将参数传递给装饰器
passing argurments to a decorator in python
我正在使用包 retrying 中的重试功能。我想从函数传递 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)
它会起作用的。但它看起来相当丑陋,并且会为每次调用构造装饰对象("regular" 装饰器在导入时执行一次)。
如果装饰器本身不是很复杂 - 您可以以更用户友好的方式使用此方法:
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)
限制:上面的代码只接受和传递装饰器的关键字参数
我正在使用包 retrying 中的重试功能。我想从函数传递 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)
它会起作用的。但它看起来相当丑陋,并且会为每次调用构造装饰对象("regular" 装饰器在导入时执行一次)。
如果装饰器本身不是很复杂 - 您可以以更用户友好的方式使用此方法:
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)
限制:上面的代码只接受和传递装饰器的关键字参数