如何编写一个返回未设置参数列表的通用Python 2.2函数?

如何编写一个返回未设置参数列表的通用Python 2.2函数?,python,jython,Python,Jython,我有一个具有许多输入参数的函数,我需要一个函数,该函数将为每个值为“”或无的参数返回一个参数名(而不是值)列表 通常我会在这种方法中抛出一个异常。如果有人想通过抛出一个异常来解决这个问题,那没关系。我仍然要求函数返回参数名列表 概括 返回未设置参数的参数名称列表 “unset”表示参数值不是空字符串或无 接受单个参数:单个维度列表或dict 该列表应包含一组完整的空参数名 我需要它与Python2.2和Jython向后兼容 2.2是不可谈判的。代码必须在我们无权升级的遗留系统上运行。我们真糟糕

我有一个具有许多输入参数的函数,我需要一个函数,该函数将为每个值为“”或无的参数返回一个参数名(而不是值)列表


通常我会在这种方法中抛出一个异常。如果有人想通过抛出一个异常来解决这个问题,那没关系。我仍然要求函数返回参数名列表

概括

  • 返回未设置参数的参数名称列表
  • “unset”表示参数值不是空字符串或无
  • 接受单个参数:单个维度列表或dict
  • 该列表应包含一组完整的空参数名
  • 我需要它与Python2.2和Jython向后兼容
  • 2.2是不可谈判的。代码必须在我们无权升级的遗留系统上运行。我们真糟糕
  • 这些参数不是命令行参数,而是 功能
  • 参数存储在单个变量中,但如果需要,我可以手动将它们放入dict中
  • 返回每个空变量的用户友好描述列表,而不是返回Python变量名列表。示例:“数据库名称”与“数据库名称”
  • 对所提问题的答复:

  • 如果遇到未知参数怎么办?我们不在乎。我们创建参数列表,以验证并仅选择那些由于系统逻辑而必须的参数。因此,我们永远不会将未知参数放入要验证的参数列表中
  • 对于非强制性或必须以其他方式验证的UI参数(int vs.string等),情况如何?我们不会将非强制性参数放在传递给验证函数的列表中。对于其他更复杂的验证,我们将单独处理这些验证。这个函数看起来很方便的原因是,空参数是我们最常用的验证方法,为每个空参数编写
    if not foo:
    在函数之间会变得单调乏味,我们有很多函数
  • 请解释“我们平台的性质”。此外,它以单个变量的形式出现。。。什么名称空间中的单个变量?“预处理”是什么意思约翰·梅辛2天前。答:变量位于全局命名空间中。我们使用代码注入(类似于C预处理器将代码替换为宏名称的方式,只是我们将变量值替换为标记,类似于:

    数据库名称=^-^将用户为数据库名称输入的变量放在此处^-^

  • 预处理器运行后,结果如下:

    DATABASE_NAME = "DB1"
    
    下面是一个具体的例子,说明了为什么一个简单的方法抛出一个异常不起作用。我重写了一个异常,而不是通过请求返回一个值:

    def validate_parameters(params_map):
        """
        map is like {foo: "this is foo"}
        """
        missing_params_info = []
        for k,v in params_map.items():
            if not k:
                missing_params_info.append(v)
        if missing_params_info:
            raise TypeError('These parameters were unset: %s' % missing_params_info)
    
    params = {}
    params['foo'] = '1'
    params['bar'] = '2'
    params['empty'] = ''
    params['empty2'] = ''
    params['None'] = None
    params_map = {
        params['foo']: 'this is foo',
        params['bar']: 'this is bar',
        params['empty']: 'this is empty',
        params['empty2']: 'this is empty2',
        params['None']: 'this is None',
    }
    
    print validate_parameters(params_map)
    
    
    bash-3.00# python /var/tmp/ck.py
    Traceback (most recent call last):
      File "/var/tmp/ck.py", line 26, in ?
        print validate_parameters(params_map)
      File "/var/tmp/ck.py", line 10, in validate_parameters
        raise TypeError('These parameters were unset: %s' % missing_params_info)
    TypeError: These parameters were unset: ['this is empty2', 'this is None']
    
    它不适用于我们的两个原因:它只打印empty2,即使还有另一个空参数“empty”。“empty”被“empty2”覆盖,因为它们在映射中使用相同的键

    第二个原因:我需要在运行这个函数后的某个时刻将描述列表放入一个变量中。也许这在例外情况下是可能的,但我现在不知道如何操作

    我发布的答案似乎解决了所有这些问题,但并不理想。我标记了所回答的问题,但如果有人发布更好的答案,我会改变这一点


    谢谢!

    要检查所有必需的参数是否都被传递到函数中,您可以创建一个字典,将所有必需的参数映射为None,然后在每个方法开始时复制并更新该字典

    needed_params = {'one': None, 'two': None, 'three': None}
    
    def my_func(**kwargs):
        params = needed_params.copy()
        params.update(kwargs)
        for key, value in params.iteritems():
            if not value:
                raise TypeError("You need to provide the argument %s" % key)
        result = do_stuff_here
        return result
    
    如评论中所述,返回“用户友好”描述可能不是一个好主意。相反,如果缺少参数,您可能希望引发错误。然后您可以在UI的其他位置处理此错误

    Kindall建议一位装修师。根据你希望检查有多复杂,我认为你可以做一些比他的建议简单一点的事情:

    def check_needed_params(target):
        needed_params = {'one': None, 'two': None, 'three': ''}
        def wrapper(*args, **kwargs):
            params = needed_params.copy()
            params.update(kwargs)
            for key, value in params.iteritems():
                if not value:
                    raise TypeError("You need to provide the argument '%s'" % key)
            return target(**params)
        return wrapper
    
    您可以使用它来识别需要检查其参数的函数,如下所示:

    @check_needed_params
    def adder(**kwargs):
        return kwargs["one"] + kwargs["two"] + kwargs["three"]
    
    然后,当调用此函数时,如果您提供所有结果,它将无缝工作,但如果您不提供,它将引发错误

    >>> adder(one=1, two=2, three=3)
    6
    >>> adder(one=1, two=2)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "c:/Users/.../python-6940fCr.py", line 8, in wrapper
    TypeError: You need to provide the argument three
    
    加法器(一=1,二=2,三=3) 6. >>>加法器(一=1,二=2) 回溯(最近一次呼叫最后一次): 文件“”,第1行,在 文件“c:/Users/../python-6940fCr.py”,第8行,在包装器中 TypeError:您需要提供三个参数
    佐伊德伯格为什么不是一个装饰师呢

    def argsnotempty(**requiredargs):
    
        def decorator(func):
    
            def wrapper(*args, **kwargs):
                code     = func.func_code
                argsreq  = code.co_argcount - 1
                argsrec  = len(args)
                posargs  = code.co_varnames[1:argsreq + 1]
                errs     = []
    
                # validate positional args
                for i, arg in enumerate(args):
                    if i == len(posargs):
                        break
                    # falsy but not False: 0, '', None, [], etc.
                    if not (arg or arg is False):
                        argname = posargs[i]
                        if argname in requiredargs:
                            errs.append(argname + " (" + requiredargs[argname] + ")")
    
                # validate keyword args
                for argname, arg in kwargs.iteritems():
                    if argname in requiredargs:
                        if not (arg or arg is False):
                            errs.append(argname + " (" + requiredargs[argname] + ")")
    
                # make sure all required args are present
                for argname in requiredargs:
                    if argname not in kwargs and argname not in posargs:
                        errs.append(argname + " (" + requiredargs[argname] + ")")
    
                return func(errs, *args, **kwargs)
    
            wrapper.__name__, wrapper.__doc__ = func.__name__, func.__doc__
    
            return wrapper
    
        return decorator
    
    decorator检查以确保指定的参数不为空,然后使用“友好”参数名称列表调用wrapped函数,该列表作为第一个参数为空。它还尝试检查关键字参数。不检查未指定给decorator的参数

    用法:

    @argsnotempty(a="alpha", b="beta", g="gamma")
    def foo(errs, a, b, g):
        print errs
    
    foo(3.14, "blarney", None)    # prints "['g (gamma)']"
    
    下面是一个在未获得所需值时引发异常的示例:

    @argsnotempty(a="alpha", b="beta", g="gamma")
    def bar(errs, a, b, g):
        if errs:
           raise ValueError("arguments " + ", ".join(errs) + " cannot be empty")
    
    bar(0, None, "")
    
    当然,您可以调整decorator来完成这项工作,而不是在每个函数中包含样板代码

    编辑:修复了一些错误

    这是一种常见的“大量变量”模式

    def function( variable1, variable2, variable3, ..., variablen ):
       """user-friendly description of the function.
       :param variable1: meaning, units, range, whatever.
       :param variable2: meaning, units, range, whatever.
       ...
       :param variablen: meaning, units, range, whatever.
       :returns: range, type, whatever.
       """
       # do the processing
    
    不要检查缺少的或无效的参数。Python已经完成了所有需要的类型检查。只需编写代码。不要执行任何特殊或额外的操作来“验证”输入。 当出现异常时,这意味着输入错误

    就这么简单

    不要在无关的if语句中重写Python的所有类型检查,从而使它变得更复杂

    还有

    切勿将“错误返回”与有效返回混为一谈。任何类型的错误输入都必须导致异常。良好输入返回良好值。错误输入引发异常

    就这么简单,不要把它弄得更复杂

    调用此函数时,可以执行以下操作:

    the_variables = { 
        'variable1': some value,
        'variable2': some value,
        ...
        'variablen': some value,
    }
    try:
        function( **the_variables )
    except Exception:
        print( function.__doc__ )
    
    有什么遗漏吗
    def validate_parameters(params_map):
        """
        map is like {foo: "this is foo"}
        """
        missing_params_info = []
        for k,v in params_map.items():
            if not v:
                missing_params_info.append(k)
        return missing_params_info
    # or do this if you want to use exceptions:
    #    if missing_params_info:
    #        raise TypeError('These parameters were unset: %s' % missing_params_info)
    
    params = {}
    params['foo'] = '1'
    params['bar'] = '2'
    params['empty'] = ''
    params['empty2'] = ''
    params['None'] = None
    
    reverse_params_map = {
        'this is foo' : params['foo'],
        'this is bar' : params['bar'],
        'this is empty' : params['empty'],
        'this is empty2' : params['empty2'],
        'this is None' : params['None'],
    }
    
    print validate_parameters(reverse_params_map)
    
    bash-3.00# python /var/tmp/ck.py
    ['this is empty2', 'this is empty', 'this is None']
    
    def noisy_typerror( func ):
        def fix_exception( **kw ):
            try:
                # This is generally needless; mostly a waste of CPU cycles.
                if not all(kw[arg] for arg in kw  ):
                    raise TypeError
                # Simply apply the function and see if a TypeError occurs 
                return func( **kw )
            except TypeError:
                required= ", ".join( func.func_code.co_varnames[:func.func_code.co_argcount] )
                provided= ", ".join( "{0}={1!r}".format(k,v) for k,v in kw.items() )
                raise TypeError( "{2}( {0} ) got {1}".format(required, provided,func.func_name) )
        return fix_exception
    
    @noisy_typerror
    def some_func( this, that, the_other ):
        a= this
        b= that
        print( this, that, the_other )
    
    def the_real_func( this, that, the_other ):
       etc.
    
    some_func= noisy_typerror( the_real_func )
    
    try:
        some_func( this=2, that=3 )
    except TypeError, e:
        print e 
    try:
        some_func( this=4 )
    except TypeError, e:
        print e 
    try:
        some_func( this=2, that=3, the_other='' )
    except TypeError, e:
        print e 
    
    some_func( this, that, the_other ) got this=2, that=3
    some_func( this, that, the_other ) got this=4
    some_func( this, that, the_other ) got this=2, the_other='', that=3
    
    def validate_parameters(args):
        unset = []
        for k in args:
            if args[k] is None or args[k]=="":
                unset.append(k)
        return unset
    
    def foo(a, b, c):
        print "Unset:", validate_parameters(locals())
    
    >>> foo(1, None, 3)
    Unset: ['b']
    >>> foo(1, None, "")
    Unset: ['c', 'b']