繁体   English   中英

如何使用带参数的条件修饰符?

[英]How to apply conditional decorator with arguments?

我在弄清楚这一点时遇到了麻烦,而且我一直遇到TypeError。 我需要一个装饰器来应用另一个仅在条件成立时才接受参数的装饰器。 TypeError与传递给outer()方法的参数有关。

def decorator(foo, bar):
    def wrapped(func):
        @wraps(func)
        def outer():
            ...stuff with foo and bar...
            return func()
        return outer
    return wrapped


def conditional(func):
    @wraps(func)
    def inner():
        if some_condition:
            raise Error
        return decorator(1, 2)(func)
    return inner

@app.route('/login', methods=['POST'])
@conditional
def login():
    ...

这会生成TypeError: outer() takes 0 positional arguments but 2 were given ,但是对于一些基本的print语句(主要是outer(*args), print(args) ),我发现它是以下内容:

第一位置:

{'wsgi.version': (1, 0), 'wsgi.url_scheme': 'http', 'wsgi.input': <_io.BufferedReader name=964>, 'wsgi.errors': <_io.TextIOWrapper name='<stderr>' mode='w' encoding='utf-8'>, 'wsgi.multithread': True, 'wsgi.multiprocess': False, 'wsgi.run_once': False, 'werkzeug.server.shutdown': <function WSGIRequestHandler.make_environ.<locals>.shutdown_server at 0x0000014341FCA0D0>, 'SERVER_SOFTWARE': 'Werkzeug/0.14.1', 'REQUEST_METHOD': 'POST', 'SCRIPT_NAME': '', 'PATH_INFO': '/login', 'QUERY_STRING': '', 'REMOTE_ADDR': '127.0.0.1', 'REMOTE_PORT': 54900, 'SERVER_NAME': '127.0.0.1', 'SERVER_PORT': '5000', 'SERVER_PROTOCOL': 'HTTP/1.1', 'HTTP_HOST': '127.0.0.1:5000', 'HTTP_USER_AGENT': 'python-requests/2.20.1', 'HTTP_ACCEPT_ENCODING': 'gzip, deflate', 'HTTP_ACCEPT': '*/*', 'HTTP_CONNECTION': 'keep-alive', 'HTTP_AUTHORIZATION': 'Basic cGF1bDpmb29iYXI=', 'CONTENT_LENGTH': '0', 'werkzeug.request': <Request 'http://127.0.0.1:5000/login' [POST]>}

第二位置:

<function run_wsgi_app.<locals>.start_response at 0x0000014341FCA378>

我的语法错误在某个地方出了问题,不确定在哪里。

通过使用*args可以解析TypeErrror,但是会出现一个新的:

TypeError: 'function' object is not iterable

由于视图调用 ,您将返回而不调用它的outer 因此Flask必须将此视为视图响应 ,并且将非字符串,元组或Response视为WSGI对象。 WSGI响应的正常处理方式是将其称为<wsgi response>(environment, start_response)

您需要返回调用outer()的实际结果。

这是在模块导入时发生的情况:

  • def login(): ...被执行,创建一个函数对象login
  • @conditional用作login的装饰器。
    • def inner(): ...被执行,创建一个嵌套函数,其闭包中带有func @wraps(func)装饰器将func的名称附加到inner
    • return inner收益inner给调用者
  • login = inner是由于@conditional设置的
  • @ app.route() registers内部registers as the route handler for / login as the route handler for

这是通过HTTP访问/login时发生的情况:

  • Flask查找/login的视图功能,找到inner ,将其调用
  • if condition:测试为假,请跳至下一部分
  • decorator(1, 2)被调用
    • def wrapped(func): ...被执行,在闭包中创建一个带有foobar的内部函数
    • return wrapped返回给调用者
  • decorator(1, 2)...wrapped...所以被wrapped(func)被称为
    • def outer(): ...被执行,创建一个内部函数,并在其闭包中带有func @wraps(func)装饰器将func的名称附加到inner
    • return outerouter函数返回给调用者。
  • outer返回给呼叫者
  • Flask被指定为outer作为响应,被视为WSGI对象。

您在这里错过了最后的通话:

def conditional(func):
    @wraps(func)
    def inner():
        if some_condition:
            raise Error
        return decorator(1, 2)(func)()  # call the decorated `func()`
    return inner

但是,除非希望防止阻止应用decorator(1, 2)调用的条件,否则要存储decorator(1, 2)(func)结果 ,而不是为每个调用修饰它:

def conditional(func):
    func = decorator(1, 2)(func)
    @wraps(func)
    def inner():
        if some_condition:
            raise Error
        return func()
    return inner

一个中间立场可能是只调用一次decorator(1, 2)一次创建实际的decorator函数:

def conditional(func):
    dec = decorator(1, 2)
    @wraps(func)
    def inner():
        if some_condition:
            raise Error
        return dec(func)()
    return inner

最后,考虑将传递给inner()参数传递给装饰的视图函数,因此可以在接受路由参数的视图函数上使用@condition

def conditional(func):
    @wraps(func)
    def inner(*args, **kwargs):
        if some_condition:
            raise Error
        return decorator(1, 2)(func)(*args, **kwargs)
    return inner

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM