[英]Python: shortcut for writing decorators which accept arguments?
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
老实说,我倾向于将我的装饰员写成类
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
如果没有一点清楚的话,它什么也没有
我知道您说的感觉不太理想,但我仍然觉得使用三个嵌套模型是最干净的解决方案。 内部的两个功能是限定用于接受参数(见的功能的装饰的只是“正常”方式例如在python的文档为@wraps)。 外部函数实际上只是一个接受参数并返回装饰器的函数。
def with_timeout(timeout):
def decorator(f):
@wraps(f)
def wrapper(*args, **kwargs):
with Timeout(timeout):
return f(*args, **kwargs)
return wrapper
return decorator
根据Jakob的建议,我实现了一个小的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)
首先,我们可以定义一个元装饰器:
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
装饰:)
不使用lambda的另一种做法:
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
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.