Flask.route()触发TypeError:意外关键字参数问题解惑
Flask装饰器参数匹配问题解析
问题背景
编写了一个猜数字的Flask应用,根路由可正常加载,但访问带数字的路由(如/1)时抛出TypeError: wrapper_function() got an unexpected keyword argument 'number'错误。
原Flask代码
from flask import Flask from random import randint rand_n = randint(0, 9) app = Flask(__name__) def decorator_header(function): def wrapper(): return f"<h1>{function()}</h1>" return wrapper def check_decorator(function): def wrapper_function(*args): result = function(args[0]) if result == "You found me!": return f"<b style='color:green;'>{result}</b>" if result == "Too low, try again!": return f"<b style='color:red;'>{result}</b>" return f"<b style='color:purple;'>{result}</b>" return wrapper_function @app.route('/') @decorator_header def guess_number(): return 'Guess a number between 0 and 9' @app.route('/<number>', endpoint='check_number') @check_decorator def check_number(number): if int(number) < rand_n: return "Too low, try again!" if int(number) > rand_n: return "Too high, try again!" return "You found me!" if __name__ == "__main__": app.run()
错误信息
[2023-02-19 21:16:23,710] ERROR in app: Exception on /1 [GET] Traceback (most recent call last): File "C:\Users\pauLo\venv\lib\site-packages\flask\app.py", line 2528, in wsgi_app response = self.full_dispatch_request() File "C:\Users\pauLo\venv\lib\site-packages\flask\app.py", line 1825, in full_dispatch_request rv = self.handle_user_exception(e) File "C:\Users\pauLo\venv\lib\site-packages\flask\app.py", line 1823, in full_dispatch_request rv = self.dispatch_request() File "C:\Users\pauLo\venv\lib\site-packages\flask\app.py", line 1799, in dispatch_request return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) TypeError: wrapper_function() got an unexpected keyword argument 'number' 127.0.0.1 - - [19/Feb/2023 21:16:23] "GET /1 HTTP/1.1" 500 -
特殊现象
- 将装饰器的
wrapper_function参数改为number后可正常运行,用其他名称仍报错; - 脱离Flask环境运行原装饰器代码无问题:
@decorator_header def guess_number(): return 'Guess a number between 0 and 9' @check_decorator def check_number(number): if int(number) < rand_n: return "Too low, try again!" if int(number) > rand_n: return "Too high, try again!" return "You found me!" guess = input(guess_number()) print(check_number(guess))
原因分析
没错,Flask确实会以关键字参数的形式传递路由参数。当访问/1时,Flask解析出路由参数number=1,调用视图函数时会执行check_number(number=1)。
原装饰器的wrapper_function只定义了*args,这只能处理位置参数,无法接收关键字参数,因此抛出“意外关键字参数”的错误。而脱离Flask环境时,你是用位置参数调用check_number(guess),*args能正常捕获参数,所以不会报错。
当你把wrapper_function的参数改为number时,它就可以接收number=1这个关键字参数,自然能正常运行。
正确解决方案
要让装饰器兼容Flask的关键字参数传递,需要让包装函数同时支持位置参数和关键字参数,修改check_decorator为:
def check_decorator(function): def wrapper_function(*args, **kwargs): result = function(*args, **kwargs) if result == "You found me!": return f"<b style='color:green;'>{result}</b>" if result == "Too low, try again!": return f"<b style='color:red;'>{result}</b>" return f"<b style='color:purple;'>{result}</b>" return wrapper_function
这种写法直接把接收到的所有参数原封不动传给原函数,不管Flask用位置参数还是关键字参数传递,装饰器都能正常处理,不需要硬编码参数名number。
内容的提问来源于stack exchange,提问作者Miqueias Brikalski
相关产品推荐
相关产品推荐

