Как построить декоратор с дополнительными параметрами?
Я хотел бы сделать декоратор, который можно использовать с параметром или без него:
Что-то вроде этого:
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'
В моем коде работает только использование декоратора с параметром: как перейти к работе (с параметром и без него)?
Ответы
Ответ 1
Я нашел пример, вы можете использовать @trace
или @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
Ответ 2
Если вы хотите взять параметры в свой декоратор, вам всегда нужно называть его как функцию:
@d()
def func():
pass
В противном случае вам нужно попытаться определить разницу в параметрах - другими словами, вам нужно магически угадать, что означает вызывающий. Не создавайте API, который нужно угадать; последовательно говорите, о чем вы хотите начать.
Другими словами, функция должна быть либо декоратором, либо декоратором factory; это не должно быть оба.
Обратите внимание, что если все, что вы хотите сделать, это сохранить значение, вам не нужно писать класс.
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'
Ответ 3
Если вы не против полагаться на использование именованных аргументов, я сделал нечто похожее на то, что вам нужно:
def cached_property(method=None, get_attribute=lambda a: '_%s_cached' % (a,)):
"""
Caches an object 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
Это работает, потому что только один аргумент без ключевого слова, декорированная функция передается декоратору.
Обратите внимание, что я также использовал аргументы, переданные декорированной функции, в этом случае "я".
Ответ 4
Вы должны определить, является ли аргумент декоратору функцией, и использовать в этом случае простой декоратор. И тогда вам нужно надеяться, что вам никогда не нужно передавать только функцию параметризованному декоратору.
Ответ 5
Это сработает.
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
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 !
Если функция декоратора @invocation не передается никакими явными аргументами, она вызывается с помощью функции, определенной в следующем def
. Если передаются аргументы, то сначала они вызываются с ними, а затем вызывается результат , который предварительный вызов (который сам должен быть вызываемым) вызывается с определяемой функцией. В любом случае возвращаемое значение последнего или единственного вызова привязано к определенному имени функции.