【问题标题】:Python decorator access argument by namePython 装饰器按名称访问参数
【发布时间】:2016-10-10 12:01:07
【问题描述】:

我想制作一个 Python 装饰器,它可以接受一个参数(它正在装饰的函数中的参数名称)并使用该参数来查找函数中参数的值。我知道这听起来非常令人困惑,哈!但我想要做的看起来像这样(这是一个 Flask 网络应用程序):

这就是我现在拥有的:

@app.route('/admin/courses/<int:course_id>')
def view_course(course_id):
    ... view code here

我想做这样的事情:

@app.route('/admin/courses/<int:course_id>')
@access_course_permission(course_id)
def view_course(course_id):
    ... view code here

现在我知道我可以这样做:

def access_course_permission(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        # check args[0] right here to find the course ID
        return f(*args, **kwargs)
    return decorated_function

只要 course_id 始终是第一个参数,它就可以很好地工作。然而,事实并非如此。这就是为什么我希望能够指定名称。

如果这不可能,我想我可以将参数的索引传递给装饰器,但这不是很好......

【问题讨论】:

    标签: python flask decorator python-decorators


    【解决方案1】:

    您可以使用inspect.getfullargspec() function 访问函数中使用的名称:

    try:
        # Python 3
        from inspect import getfullargspec
    except ImportError:
        # Python 2, use inspect.getargspec instead
        # this is the same function really, without support for annotations
        # and keyword-only arguments
        from inspect import getargspec as getfullargspec
    
    from functools import wraps
    
    def access_course_permission(argument_name):
        def decorator(f):
            argspec = getfullargspec(f)
            argument_index = argspec.args.index(argument_name)
            @wraps(f)
            def wrapper(*args, **kwargs):
                try:
                    value = args[argument_index]
                except IndexError:
                    value = kwargs[argument_name]
                # do something with value
                return f(*args, **kwargs)
            return wrapper
        return decorator
    

    上面找出了您的特定参数位于哪个索引;这涵盖了位置参数和关键字参数(因为在 Python 中,您也可以按位置为关键字参数传递值)。

    但是请注意,对于您的具体示例,Flask 将使用 course_id 作为 关键字参数 调用 view_course,因此使用 kwargs[argument_name] 就足够了。

    您必须传入一个 字符串 来命名该参数:

    @app.route('/admin/courses/<int:course_id>')
    @access_course_permission('course_id')
    def view_course(course_id):
        # ... view code here
    

    但是请注意,在 Flask 中,您可以直接访问 request.view_args,而无需从函数参数中解析这些信息:

    course_id = requests.view_args[argument_name]
    

    【讨论】:

    • inspect.getargspec() 自 Python 3.5 起已被弃用,您可以使用 inspect.getfullargspec() 代替以达到相同的效果
    • @JoeAlamo:实际上,它从 3.0 开始就被弃用了。 :-) 但是感谢您的提醒,我现在已经使代码 Python 版本不可知。
    • 看起来结果装饰器不能多次使用,因为命名参数将不再在包装函数中命名。 :|
    • @Eraldo:不知道为什么要单独使用装饰器?在调用 getfullargspec() 之前,您始终可以使用 inspect.unwrap() 删除装饰器。
    • @MartijnPieters 想象有两个装饰器:has_role(r)in_group(g)。如果我堆叠这些,我会丢失 info 参数,第二个会引发错误。你明白我的意思吗? :)
    猜你喜欢
    • 2011-06-25
    • 1970-01-01
    • 2011-09-22
    • 2021-09-14
    • 2020-06-20
    • 2014-05-01
    • 2014-03-27
    • 1970-01-01
    • 2019-06-14
    相关资源
    最近更新 更多