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