Передача параметра декоратору в python
Почему этот декоратор с параметром не работает?
def decAny( f0 ):
def wrapper( s0 ):
return "<%s> %s </%s>" % ( any, f0(), any )
return wrapper
@decAny( 'xxx' )
def test2():
return 'test1XML'
print( test2() )
всегда выдает ошибку, говоря, что "str не вызывается", он пытается выполнить возвращаемую строку внутри оболочки () вместо того, чтобы обработать ее и вернуть строку результата
3 ответа
Декораторы - это функции, которые возвращают функции. Когда вы "передаете параметр декоратору", вы фактически вызываете функцию, которая возвращает декоратор. Так decAny()
должна быть функция, которая возвращает функцию, которая возвращает функцию.
Это будет выглядеть примерно так:
import functools
def decAny(tag):
def dec(f0):
@functools.wraps(f0)
def wrapper(*args, **kwargs):
return "<%s> %s </%s>" % (tag, f0(*args, **kwargs), tag)
return wrapper
return dec
@decAny( 'xxx' )
def test2():
return 'test1XML'
Пример:
>>> print(test2())
<xxx> test1XML </xxx>
Обратите внимание, что в дополнение к исправлению конкретной проблемы, с которой вы столкнулись, я также немного улучшил ваш код, добавив *args
а также **kwargs
в качестве аргументов для упакованной функции и передачи их f0
позвоните внутрь декоратора. Это позволяет вам украшать функцию, которая принимает любое количество позиционных или именованных аргументов, и она все равно будет работать правильно.
Вы можете прочитать о functools.wraps()
Вот:
http://docs.python.org/2/library/functools.html
Вот хороший пример из книги "Mark Lutz - Learning Python":
def timer(label=''):
def decorator(func):
def onCall(*args): # Multilevel state retention:
... # args passed to function
func(*args) # func retained in enclosing scope
print(label, ... # label retained in enclosing scope
return onCall
return decorator # Returns the actual decorator
@timer('==>') # Like listcomp = timer('==>')(listcomp)
def listcomp(N): ... # listcomp is rebound to new onCall
listcomp(...) # Really calls onCall
есть еще один способ реализации декоратора с использованием класса, и вы также можете передавать аргументы самому декоратору
вот пример вспомогательного декоратора логгера, которому вы можете передать область действия функции и возвращаемое значение в случае сбоя
import logging
class LoggerHelper(object):
def __init__(self, scope, ret=False):
self.scope = scope
self.ret = ret
def __call__(self, original_function):
def inner_func(*args, **kwargs):
try:
logging.info(f"*** {self.scope} {original_function.__name__} Excuting ***")
return original_function(*args, **kwargs)
logging.info(f"*** {self.scope} {original_function.__name__} Executed Successfully ***")
except Exception as e:
logging.error(f"*** {self.scope} {original_function.__name__} Error: {str(e)} ***")
return self.ret
return inner_func
и когда вы его используете, вы можете легко отследить, где было возбуждено исключение
class Example:
@LoggerHelper("Example", ret=False)
def method:
print(success)
return True