[英]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设置的 registers
内部registers
as the route handler for
/ login as the route handler for
这是通过HTTP访问/login
时发生的情况:
/login
的视图功能,找到inner
,将其调用 if condition:
测试为假,请跳至下一部分 decorator(1, 2)
被调用
def wrapped(func): ...
被执行,在闭包中创建一个带有foo
和bar
的内部函数 return wrapped
返回给调用者 decorator(1, 2)...
被wrapped...
所以被wrapped(func)
被称为
def outer(): ...
被执行,创建一个内部函数,并在其闭包中带有func
。 @wraps(func)
装饰器将func
的名称附加到inner
。 return outer
将outer
函数返回给调用者。 outer
返回给呼叫者 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.