如何在适当的位置键入转换函数的多个参数?
How can I type convert many arguments of a function in place?
上下文
我使用 CherryPy 来提供一个简单的网页,该网页根据 URL 参数显示不同的内容。具体来说,它采用参数的总和并基于此显示不同的消息。
在 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", b="0", c="0", d="0", e="0", f="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
装饰器(最初灵感来自 ):
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, b, c, d, e
print(f(1, 2, d=7))
# Output: ('1', 2, 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', 'c', 'd', '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),
)
P.S。在您最初的 post 中,您使用 return print(...)
这是错误的。 print()
总是 returns None
所以你会把 "None"
发送回 HTTP 客户端,而 print(arg)
的参数只会在你的终端打印出来你 运行 服务器。
上下文
我使用 CherryPy 来提供一个简单的网页,该网页根据 URL 参数显示不同的内容。具体来说,它采用参数的总和并基于此显示不同的消息。
在 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", b="0", c="0", d="0", e="0", f="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
装饰器(最初灵感来自 ):
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, b, c, d, e
print(f(1, 2, d=7))
# Output: ('1', 2, 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', 'c', 'd', '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),
)
P.S。在您最初的 post 中,您使用 return print(...)
这是错误的。 print()
总是 returns None
所以你会把 "None"
发送回 HTTP 客户端,而 print(arg)
的参数只会在你的终端打印出来你 运行 服务器。