Добавить декоратор из строки

#python #python-3.x #python-decorators

#питон #python-3.x #python-декораторы

Вопрос:

У меня есть такой декоратор:

 def foo(func):
    """Will print the args and kwargs of a func"""
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print('Do I have args?')
        print(args, kwargs, sep='n')

        return func(*args, **kwargs)

    return wrapper
 

И такой «создатель декораторов», как этот:

 def add_checks(*decorators):
    """Adds decorators to a func"""
    def check(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            # how do I add here the `*decorators`?
            return func(*args, **kwargs)

        return wrapper

    return check
 

Как мне добавить декораторы по строке? Итак, это что-то вроде этого:

 @add_checks('foo')
def bar(a, b, c=1):
    return (a * b) / c

print(bar(5, 2, c=5))
>>> Do I have args?
>>> [5, 2]
>>> {'c': 5}
>>> 2
 

Комментарии:

1. Есть ли причина, по которой вы хотите индексировать свои декораторы по строкам, а не просто использовать встроенные ссылки на функции?

Ответ №1:

Вам просто нужно перебрать *decorators :

 import functools


def foo(func):
    """Will print the args and kwargs of a func"""
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print('Do I have args?')
        print(args, kwargs, sep='n')
        #return func(*args, **kwargs)

    return wrapper


def add_checks(*decorators):
    """Adds decorators to a func"""
    def check(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            for dec in decorators:
                if isinstance(dec, str):
                    # lookup for the function in globals()
                    globals()[dec](func)(*args, **kwargs)
                else:
                    dec(func)(*args, **kwargs)

            return func(*args, **kwargs)

        return wrapper

    return check


@add_checks(foo)  # reference to the function!
def bar(a, b, c=1):
    return (a * b) / c


@add_checks("foo")
def bar_strDecorated(a, b, c=1):
    return (a * b) / c


print(bar(5, 2, c=5))
print(bar_strDecorated(5, 2, c=5))
 

Out:

 Do I have args?
(5, 2)
{'c': 5}
2.0
Do I have args?
(5, 2)
{'c': 5}
2.0
 

Комментарии:

1. Хорошее решение, хотя не должны ли обернутые функции располагаться друг внутри друга, а не просто вызываться одна за другой? т.Е. Первый декоратор должен иметь возможность изменять аргументы / kwargs, передаваемые следующему, и так далее. Я ценю, что OP не посвятил нас в грандиозный план для этой системы, поэтому я остаюсь в догадках!

2. Человек globals , поиск ключа просто выглядит странно, eval на самом деле лучше подойдет здесь? В противном случае мне нравится решение

3. Лично мне ненавистна идея поиска кода с использованием глобалов или иным образом. Использование ссылок на функции (как сказал Морис) — это правильный путь вперед. Но если вы собираетесь это сделать, зачем вообще использовать этот шаблон? Просто сложите декораторов.