17

我希望仅当登录用户具有所需的权限级别时才能执行不同的功能。

为了让我的生活更简单,我想使用装饰器。下面我尝试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'

我错过了什么?

4

3 回答 3

26

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

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,使其更容易使用。

于 2013-04-03T17:51:04.847 回答
1

你的装饰器应该返回一个可以替换do_xor的函数do_y,而不是返回 or 的执行结果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
于 2013-04-03T18:02:01.577 回答
0

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

您应该只返回添加了属性的相同原始函数,因此您真的不想担心这个原始修饰函数可能采用哪些参数,这意味着您可以摆脱其中一个包装级别:

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'
于 2019-09-25T20:44:45.703 回答