用于设置函数属性的装饰器



我希望不同的函数只有在登录用户具有所需的权限级别时才能执行。

为了让我的生活更简单,我想使用装饰器。下面我尝试在'装饰'函数上设置属性permission -如下所示。

def permission(permission_required):
    def wrapper(func):
        def inner(*args, **kwargs):
            setattr(func, 'permission_required', permission_required)
            return func(*args, **kwargs)
        return inner
    return wrapper
@permission('user')
def do_x(arg1, arg2):
    ...
@permission('admin')
def do_y(arg1, arg2):
    ...

但是当我这样做的时候:

fn = do_x
if logged_in_user.access_level == fn.permission_required:
    ...

我得到一个错误'function' object has no attribute 'permission_required'

我错过了什么?

您正在检查内部(包装)函数上的属性,但将其设置在原始(包装)函数上。但是你需要一个包装器函数:

def permission(permission_required):
    def decorator(func):
        func.permission_required = permission_required
        return func
    return decorator

你的装饰器需要返回一些将取代原来的函数。原来的函数本身(添加了属性)可以很好地做到这一点,因为您所要做的就是为它添加一个属性。

如果您仍然需要一个包装器,那么在包装器函数上设置该属性:

from functools import wraps
def permission(permission_required):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            # only use a wrapper if you need extra code to be run here
            return func(*args, **kwargs)
        wrapper.permission_required = permission_required
        return wrapper
    return decorator

毕竟,您正在用装饰器返回的包装器替换包装函数,因此这就是您将在其上查找属性的对象。

我还向包装器添加了@functools.wraps()装饰器,它将重要的标识信息和其他有用的东西从func复制到包装器,使其更容易使用。

您的decorator应该返回一个可以替换do_xdo_y的函数,而不是返回do_xdo_y的执行结果你可以这样修改你的装饰:

def permission(permission_required):
    def wrapper(func):
        def inner():
            setattr(func, 'permission_required', permission_required)
            return func
        return inner()
    return wrapper

当然,您还有另一个简单的解决方案:

def permission(permission_required):
    def wrapper(func):
        setattr(func, 'permission_required', permission_required)
        return func
    return wrapper

问题是,即使您在inner中为包装函数设置了所需的属性,inner也会返回由装饰函数返回的任何内容,而这些内容通常不会是函数本身。

您应该只返回添加了属性的完全相同的原始函数,因此您不需要真正担心这个原始修饰函数可能接受什么参数,这意味着您可以摆脱一个包装级别:

def permission(permission_required):
   def wrapper(func):
       setattr(func, 'permission_required', permission_required)
       return func
   return wrapper
@permission('user')
def do_x(arg1, arg2):
    pass
@permission('admin')
def do_y(arg1, arg2):
    pass

这个可以正常工作:

>>> do_x
<function __main__.do_x(arg1, arg2)>
>>> do_x.permission_required
'user'
>>> do_y.permission_required
'admin'

相关内容

  • 没有找到相关文章

最新更新