我在弄清楚这一点时遇到了麻烦,而且我一直遇到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
,但是通过一些基本的打印语句(主要是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
答案 0 :(得分:3)
作为视图调用的结果,您将返回outer
,而没有调用 。因此Flask必须treat this as a view response,并且不是字符串,元组或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
内部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)
,一次创建实际的装饰器函数:
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