如何在适当的位置键入转换函数的多个参数? 哑巴循环 locals()

问题描述

上下文

我使用 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) 的参数将在您运行服务器的终端中打印出来.