Как я могу сделать несколько @обертываний меньше?

#python #functools

Вопрос:

Я действительно недостаточно (пока) знаю о python, чтобы разобраться в этом самостоятельно, поэтому я хотел попробовать здесь. Есть ли какой-нибудь способ сделать так, чтобы эти почти идентичные функции @wraps занимали меньше места? Всего у меня их 5, и 100 строк для 5 раз одно и то же звучит как пустая трата времени. Первоначально я нашел это на каком-то веб-сайте, но, похоже, больше не могу найти.

Функции:

 def a_required(func):
    @wraps(func)
    def decorated_view(*args, **kwargs):
        if request.method in EXEMPT_METHODS:
            return func(*args, **kwargs)
        elif current_app.config.get('LOGIN_DISABLED'):
            return func(*args, **kwargs)
        elif not current_user.is_authenticated or not current_user["Keys"]["A"]:
            return current_app.login_manager.unauthorized()
        return func(*args, **kwargs)
    return decorated_view

def b_required(func):
    @wraps(func)
    def decorated_view(*args, **kwargs):
        if request.method in EXEMPT_METHODS:
            return func(*args, **kwargs)
        elif current_app.config.get('LOGIN_DISABLED'):
            return func(*args, **kwargs)
        elif not current_user.is_authenticated or not current_user["Keys"]["B"]:
            return current_app.login_manager.unauthorized()
        return func(*args, **kwargs)
    return decorated_view
 

Это для веб-сайта Flask, страницы которого будут доступны только пользователям с соответствующими правами.

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

1. Вы можете написать функцию «более высокого порядка», которая возвращает такую функцию декоратора, но с ключом (a, b,…), о котором идет речь, в качестве параметра внешней функции.

2. Не могли бы вы привести пример?

Ответ №1:

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

 def required(req):
    def wrapper(func):
        @wraps(func)
        def decorated_view(*args, **kwargs):
            # put your decorated_view code here
            #  swapping out the hard coded `current_user["Keys"]["B"]`
            #  for `current_user["Keys"][req]`

            print("executing decorator with", req)
            return func(*args, **kwargs)
        return decorated_view
    return wrapper

@required("B")
def foo():
    print("inside foo function")
    
@required("A")
def bar():
    print("inside bar function")
 

Тогда выполнение этих функций выглядит следующим образом:

 >>> foo()
executing decorator with B
inside foo function

>>> bar()
executing decorator with A
inside bar function
 

Функция required возвращает динамический декоратор, который изменяет свое поведение в зависимости от значения req , которое мы ему передаем. Таким образом, decorated_view функция может получить доступ к соответствующему значению req в зависимости от того, как мы вызвали required(...) .

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

1. nonlocal Заявление здесь не требуется, так как вы не пишете req .

2. хорошая мысль, я удалил ее из фрагмента.

Ответ №2:

попробуйте это:

 def required(req):
    def wrapper(func):
        @wraps(func)
        def decorated_view(*args, **kwargs):
            if not current_user.is_authenticated or not current_user["Keys"][req]:
                return current_app.login_manager.unauthorized()
            return func(*args, **kwargs)
        return decorated_view
    return wrapper
    
@required("A")
def method1():
    pass
    
@required("B")
def method2():
    pass