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

时间:2011-12-02 18:13:01

标签: python jython

我有一个带有许多输入参数的函数,我需要一个函数来为每个参数返回一个参数名称列表(而不是值),这些参数的值为''或无

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

总结

  1. 返回未设置参数的参数名称列表
  2. “unset”表示参数的值不是空字符串或无
  3. 接受单个参数:单维列表或字典
  4. 该列表应包含完整的空参数名称
  5. 我需要它向后兼容Python 2.2和Jython
  6. 2.2是不可协商的。代码必须在我们无权升级的遗留系统上运行。很难成为我们。
  7. 参数不是命令行参数,而是参数 的功能。
  8. 参数存储在各个变量中,但如果需要,我可以手动将它们放入dict中。
  9. 不返回Python变量名列表,而是返回每个空变量的用户友好描述列表。示例:“数据库名称”与“db_name”。
  10. 提出问题的答案:

    1. 如果遇到未知参数怎么办?我们不在乎。我们创建要验证的参数列表,并根据系统的逻辑仅选择那些必需的参数。因此,我们永远不会将未知参数放入要验证的列表中
    2. 那些非强制性或必须以其他方式验证的UI参数(int与字符串等)如何?我们不会将非强制性参数放在我们传递给验证函数的列表中。对于其他更复杂的验证,我们会单独处理这些问题。这个函数看起来很方便的原因是因为空参数是我们最常见的验证,并且为每个函数编写if not foo:在函数之间变得繁琐,我们有很多函数。
    3. 请解释“”“我们平台的性质”“”。还“”“它到达个别变量”“”...个别变量在什么名称空间?什么“”“(预处理)”“”是什么意思? - John Machin 2天前。答案:变量位于全局命名空间中。我们使用代码注入(类似于C预处理器如何用代码代替宏名,除了我们用变量值替换标签,类似于:

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

    4. 在预处理器运行后以此结束:

      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”被“empty2”覆盖,因为它们在地图中使用相同的键。

      第二个原因:我需要在运行此函数后的某个时刻将描述列表添加到变量中。也许这有可能是例外,但我现在不知道如何。

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

      谢谢!

7 个答案:

答案 0 :(得分:2)

我很确定我不理解这个问题,或者您发布的“最佳解决方案”如何符合要求,但仅仅是从以下方面开始:

  

我有一个带有许多输入参数的函数,我需要一个函数   这将返回每个参数名称列表(而不是值)   值为''或无

的参数

这是一个简单的方法来做这条线似乎要求:

def validate_parameters(args):
    unset = []
    for k in args:
        if args[k] is None or args[k]=="":
            unset.append(k)
    return unset

然后只需从函数的第一行行调用validate_parameters:

def foo(a, b, c):
    print "Unset:", validate_parameters(locals())

>>> foo(1, None, 3)
Unset: ['b']
>>> foo(1, None, "")
Unset: ['c', 'b']

如果不是Python 2.2的要求,你可以在单行列表理解中完成所有操作。重要的是你必须从函数的第一行调用它,以确保locals()只获取参数而不是任何其他局部变量。

答案 1 :(得分:1)

为什么不 Zoidberg 装饰者?

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

装饰器检查以确保指定的参数不为空,然后使用" friendly"的列表调用包装函数。参数名称为空白作为第一个参数。它还尝试检查关键字参数。没有为装饰者指定的参数也不会被检查。

用法:

@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, "")

当然,您可以调整装饰器为您执行此操作,而不是在每个函数中包含样板代码。

编辑:修正了一些buggage

答案 2 :(得分:0)

要检查是否所有必需参数都传递到您的函数中,您可以创建一个将所有必需参数映射到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

答案 3 :(得分:0)

这是一个常见的&#34;很多变量&#34;图案。

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已经完成了所有需要的类型检查。只需编写代码即可。做什么特别的或额外的&#34;验证&#34;输入。 当出现异常时,这意味着输入很糟糕。

就这么简单。

通过在无关的if语句中重写所有Python类型检查,不要让它变得更复杂。

另外。

永远不要混合&#34;错误返回&#34;有效的回报。任何类型的错误输入都必须导致异常。好的投入会带来良好的价值糟糕的输入会引发异常。

就是这么简单。不要让它变得更复杂。

调用此功能时,您可以执行以下操作:

the_variables = { 
    'variable1': some value,
    'variable2': some value,
    ...
    'variablen': some value,
}
try:
    function( **the_variables )
except Exception:
    print( function.__doc__ )

有什么遗漏?你得到一个TypeError。有什么不正确的None或空的?你得到一个ValueError(或者它取决于TypeError)。

出现问题时,您可以打印该功能的用户友好描述。

这非常有效,并且根本不需要编程。

答案 4 :(得分:0)

你的功能听起来很大。您是否考虑过将其分解或将其变为单独的课程是否合适?

答案 5 :(得分:0)

这是我能想到的最佳答案。它需要在调用函数之前做很多准备工作,所以我不喜欢它。但是,它符合所有要求。

感谢所有参与的人,我很抱歉这个问题需要这么多修改!

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']

答案 6 :(得分:0)

这个问题仍然令人困惑。非常。

可能是你要求对函数的代码对象进行内省:

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 )

在旧版本的Python中应用装饰器

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 

我通过打印TypeError字符串得到了这些结果。

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