Python 如何使用可选参数构建装饰器?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3931627/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to build a decorator with optional parameters?
提问by Eric
I would like to make a decorator which could be used with or without a parameter : Something like this :
我想做一个可以带或不带参数使用的装饰器:像这样:
class d(object):
def __init__(self,msg='my default message'):
self.msg = msg
def __call__(self,fn):
def newfn():
print self.msg
return fn()
return newfn
@d('This is working')
def hello():
print 'hello world !'
@d
def too_bad():
print 'does not work'
In my code, only the use of decorator with parameter is working: How to proceed to have both working (with and without parameter)?
在我的代码中,只有使用带参数的装饰器才有效:如何继续工作(带和不带参数)?
采纳答案by Eric
I found an example, you can use @traceor @trace('msg1','msg2'): nice!
我找到了一个例子,你可以使用@traceor @trace('msg1','msg2'):nice!
def trace(*args):
def _trace(func):
def wrapper(*args, **kwargs):
print enter_string
func(*args, **kwargs)
print exit_string
return wrapper
if len(args) == 1 and callable(args[0]):
# No arguments, this is the decorator
# Set default values for the arguments
enter_string = 'entering'
exit_string = 'exiting'
return _trace(args[0])
else:
# This is just returning the decorator
enter_string, exit_string = args
return _trace
回答by Ignacio Vazquez-Abrams
You have to detect if the argument to the decorator is a function, and use a simple decorator in that case. And then you need to hope that you never need to pass only a function to the parametrized decorator.
您必须检测装饰器的参数是否为函数,并在这种情况下使用简单的装饰器。然后你需要希望你永远不需要只将一个函数传递给参数化装饰器。
回答by Glenn Maynard
If you want to take parameters to your decorator, you need to alwayscall it as a function:
如果您想将参数传递给装饰器,则需要始终将其作为函数调用:
@d()
def func():
pass
Otherwise, you need to try to detect the difference in parameters--in other words, you need to magically guess what the caller means. Don't create an API that needs to guess; consistently say what you mean to begin with.
否则,您需要尝试检测参数的差异——换句话说,您需要神奇地猜测调用者的意思。不要创建需要猜测的 API;始终如一地说出你的意思。
In other words, a function should either be a decorator, or a decorator factory; it shouldn't be both.
换句话说,一个函数要么是一个装饰器,要么是一个装饰器工厂;不应该两者兼而有之。
Note that if all you want to do is store a value, you don't need to write a class.
请注意,如果您只想存储一个值,则无需编写类。
def d(msg='my default message'):
def decorator(func):
def newfn():
print msg
return func()
return newfn
return decorator
@d('This is working')
def hello():
print 'hello world !'
@d()
def hello2():
print 'also hello world'
回答by martineau
This would work.
这会奏效。
def d(arg):
if callable(arg):
def newfn():
print('my default message')
return arg()
return newfn
else:
def d2(fn):
def newfn():
print(arg)
return fn()
return newfn
return d2
@d('This is working')
def hello():
print('hello world !')
@d # No explicit arguments.
def hello2():
print('hello2 world !')
@d('Applying it twice')
@d('Would also work')
def hello3():
print('hello3 world !')
hello()
hello2()
hello3()
Output:
输出:
This is working
hello world !
my default message
hello2 world !
Applying it twice
Would also work
hello3 world !
If a decorator function @invocation isn't passed any explicit arguments, it is called with the function defined in the following def. If it ispassed arguments, then it is first called with them and then the result of thatpreliminary call (which must itself also be a callable) is called with the function being defined. Either way, the return value of the last or only call is bound to the defined function name.
如果装饰器函数@调用未传递任何显式参数,则使用以下定义的函数调用它 def。如果它被传递参数,则首先使用它们调用它,然后使用正在定义的函数调用该初步调用的结果(它本身也必须是可调用的)。无论哪种方式,最后一次或唯一一次调用的返回值都绑定到定义的函数名称。
回答by ricardo
If you don't mind relying on using named arguments, I made something similar to what you need:
如果你不介意依赖使用命名参数,我做了一些类似于你需要的东西:
def cached_property(method=None, get_attribute=lambda a: '_%s_cached' % (a,)):
"""
Caches an object's attribute.
Can be used in the following forms:
@cached_property
@cached_property()
@cached_property(get_attribute=lambda x: 'bla')
@param method: the method to memoizes
@param get_attribute: a callable that should return the cached attribute
@return a cached method
"""
def decorator(method):
def wrap(self):
private_attribute = get_attribute(method.__name__)
try:
return getattr(self, private_attribute)
except AttributeError:
setattr(self, private_attribute, method(self))
return getattr(self, private_attribute)
return property(wrap)
if method:
# This was an actual decorator call, ex: @cached_property
return decorator(method)
else:
# This is a factory call, ex: @cached_property()
return decorator
This works because only one non keyword argument, the function decorated is passed to the decorator.
这是有效的,因为只有一个非关键字参数,装饰的函数被传递给装饰器。
Notice that I also used the arguments passed to the decorated function, in this case 'self'.
请注意,我还使用了传递给装饰函数的参数,在本例中为“self”。

