Можно ли использовать Python-декораторы для изменения поведения функции, возвращаемой функцией? - PullRequest
4 голосов
/ 16 октября 2019

У меня есть функции, которые возвращают функции валидатора, простой пример:

def check_len(n):
    return lambda s: len(s) == n

Можно ли добавить декоратор, который выводит сообщение, если проверка оценивается как ложная? Примерно так:

@log_false_but_how
def check_len(n):
    return lambda s: len(s) == n

check_one = check_len(1)
print(check_one('a')) # returns True
print(check_one('abc')) # return False

Ожидаемый результат:

True
validator evaluated to False
False

Я пытался создать аннотацию, но с помощью нее могу получить доступ только к созданию функции. Одним из способов было бы определить функции следующим образом:

def log_false(fn):
    def inner(*args):
        res = fn(*args)
        if not res:
            print("validation failed for {}".format(fn.__name__))
        return res
    return inner


@log_false
def check_one(s):
    return check_len(1)(s)

Но при этом мы теряем динамическое создание функций проверки.

1 Ответ

3 голосов
/ 16 октября 2019

Вы делаете проверку не в том месте. check_len является фабрикой функций, поэтому res не является логическим значением - это функция. Ваш @log_false декоратор должен обернуть функцию валидатора вокруг каждой лямбды, возвращаемой check_len. В основном вам нужно написать декоратор, который декорирует возвращенные функции.

def log_false(validator_factory):
    # We'll create a wrapper for the validator_factory
    # that applies a decorator to each function returned
    # by the factory

    def check_result(validator):
        @functools.wraps(validator)
        def wrapper(*args, **kwargs):
            result = validator(*args, **kwargs)
            if not result:
                name = validator_factory.__name__
                print('validation failed for {}'.format(name))
            return result
        return wrapper

    @functools.wraps(validator_factory)
    def wrapper(*args, **kwargs):
        validator = validator_factory(*args, **kwargs)
        return check_result(validator)
    return wrapper

Результат:

@log_false
def check_len(n):
    return lambda s: len(s) == n

check_one = check_len(1)
print(check_one('a')) # prints nothing
print(check_one('abc')) # prints "validation failed for check_len"
...