我应该怎么写装饰器?

时间:2015-01-23 11:11:36

标签: python django python-decorators

在我的遗留代码中,我发现了两种编写视图装饰器的方法。 第一:

def require_session(f):
    def wrap(request, *args, **kwargs):
        if not 'username' in request.session:
            messages.warning(request,_('You have to login first before you can access this page.'))
            return redirect('/login')
        return f(request, *args, **kwargs)
    wrap.__doc__=f.__doc__
    wrap.__name__=f.__name__
    return wrap

和第二名:

def require_role(role):
    def decorator(func):
        def inner_decorator(request,*args, **kwargs):
            user_role = request.session.get('role','user')
            if user_role != role:
                if user_role == 'user':
                    return redirect('/dashboard')
                else:
                    return redirect('/dashboard/list_users')
            return func(request, *args, **kwargs)
        return wraps(func)(inner_decorator)
    return decorator

哪一个更好,更“pythonic”? 或者我应该以完全不同的方式编写它?你写装饰器的方法是什么?

2 个答案:

答案 0 :(得分:3)

这些做不同的事情。第一个是标准装饰器,即它包装一个函数并返回包装版本,在调用原始函数之前调用它时会执行一些操作。它以标准方式使用:

@require_session
def function_that_requires_session(request):
    ...

第二个是参数化装饰器。也就是说,装饰器本身需要一个参数 - 在这种情况下,需要确切的角色 - 因此定义需要接受一个参数,然后返回一个实际的装饰器,它与标准相同上面的版本。这就是为什么你有一个额外级别的嵌套函数。所以,这将使用这样:

@require_role(role_that_is_required)
def function_that_requires_role(request):
    ...

如您所见,装饰器采用了一个参数。

两者之间的另一个区别是第一个是使用手动方式将名称/文档字符串传递给修饰函数,而第二个使用functools.wraps()。你应该做后者(虽然lvc指出,你可以将它用作装饰器,甚至更清晰)。

答案 1 :(得分:1)

处理复制文档字符串和其他元数据的常用方法是使用functools.wraps,就像在第二个示例中一样,除了可以使用其返回值作为装饰器

def my_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        # do stuff

    return wrapper