问题描述
上下文
我使用 CherryPy 来提供一个简单的网页,该网页根据网址参数显示不同的内容。具体来说,它取参数的总和并基于此显示不同的消息。
在 CherryPy 中,网页可以定义为函数,URL 参数作为参数传递给该函数。
由于 explained in this tutorial URL 参数作为字符串传递,因此为了计算总和,我想将每个参数转换为浮点数。我会有很多URL参数,所以一个一个的做起来似乎很麻烦。
如何在适当位置输入转换(大量)参数?
我的尝试
哑巴
def dumb(a="0",b="0",c="0",d="0",e="0",f="0",g="0"):
a = float(a)
b = float(b)
c = float(c)
d = float(d)
e = float(e)
f = float(f)
g = float(g)
return print(sum([a,b,c,d,e,f,g]))
它具有可读性,但相当重复,而且不是很“pythonic”。
循环 locals()
我发现的另一个选择是将本地人重新分配给字典,然后循环遍历它并从字典中调用值。
def looping_dict(a="0",g="0"):
args = locals()
for key in args:
if key in ["a","b","c","d","e","f","g"]:
args[key] = float(args[key])
return print(sum([args["a"],args["b"],args["c"],args["d"],args["e"],args["f"],args["g"]] ) )
这有点烦人,因为我每次都必须参考字典。所以一个简单的引用 d
变成了 args["d"]
。也无助于代码可读性。
解决方法
这是我之前用于类似事物的 @convert
装饰器(最初受 https://stackoverflow.com/a/28268292/4597523 启发):
import functools,inspect
def convert(*to_convert,to):
def actual_convert(fn):
arg_names = inspect.signature(fn).parameters.keys()
@functools.wraps(fn)
def wrapper(*args,**kwargs):
args_converted = [to(arg) if arg_name in to_convert else arg
for arg,arg_name in zip(args,arg_names)]
kwargs_converted = {kw_name: to(val) if kw_name in to_convert else val
for kw_name,val in kwargs.items()}
return fn(*args_converted,**kwargs_converted)
return wrapper
return actual_convert
@convert('a','c','d',to=str)
def f(a,b,c=5,*,d,e=0):
return a,c,e
print(f(1,2,d=7))
# Output: ('1',5,'7',0)
# Passed params `a` and `d` got changed to `str`,# however `c` used the default value without conversion
它使用 inspect.signature
来获取非关键字参数名称。我不确定 CherryPy 如何传递参数或如何获取名称,但这可能是一个坚实的开始。使用 functools.wraps
很重要 - 它确保保留原始签名函数签名,这对于 CherryPy 似乎很重要。
这仅记录在变更日志中,但自 2016 年以来 with cherrypy >= 6.2.0
there is a @cherrypy.tools.params
tool 完全按照您的意愿行事(前提是您使用支持类型注释的 Python 3 版本):
import cherrypy
@cherrypy.tools.params()
def your_http_handler(
a: float = 0,b: float = 0,c: float = 0,d: float = 0,e: float = 0,f: float = 0,g: float = 0,):
return str(a + b + c + d + e + f + g)
添加它的 PR 是 PR #1442 — 您可以通过查看那里的测试来探索用法。
如果您的 Python 由于某种原因过时,您可以这样做:
import cherrypy
def your_http_handler(**kwargs):
# Validate that the right query args are present in the HTTP request:
if kwargs.keys() ^ {'a','b','e','f','g'}:
raise cherrypy.HTTPError(400,message='Got invalid args!')
numbers = (float(num) for num in kwargs.values()) # generator expression won't raise conversion errors here
try:
return str(sum(numbers)) # this will actually call those `float()` conversions so we need to catch a `ValueError`
except ValueError as val_err:
raise cherrypy.HTTPError(
400,message='All args should be valid numbers: {exc!s}'.format(exc=val_err),)
附言在您最初的帖子中,您使用了错误的 return print(...)
。 print()
始终返回 None
,因此您会将 "None"
发送回 HTTP 客户端,而 print(arg)
的参数将在您运行服务器的终端中打印出来.