Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
145 views
in Technique[技术] by (71.8m points)

python - decorator to set attributes of function

I want different functions to be executable only if the logged in user has the required permission level.

To make my life more simple I want to use decorators. Below I attempt to set attribute permission on 'decorated' functions - as shown below.

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):
    ...

But when I do:

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

I get an error 'function' object has no attribute 'permission_required'

What am I missing?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You are checking the attribute on the inner (wrapper) function, but set it on the original (wrapped) function. But you need a wrapper function at all:

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

Your decorator needs to return something that'll replace the original function. The original function itself (with the attribute added) will do fine for that, because all you wanted to do is add an attribute to it.

If you still need a wrapper, then set the attribute on the wrapper function instead:

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

After all, you are replacing the wrapped function with the wrapper returned by the decorator, so that's the object you'll be looking for the attribute on.

I also added the @functools.wraps() decorator to the wrapper, which copied across important identifying information and other helpful things from func to the wrapper, making it much easier to work with.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...