Передача параметра декоратору в 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
Другие вопросы по тегам