我试图将异常提升代码块编写到我的Python代码中,以确保传递给函数的参数满足适当的条件(即使参数成为必需,类型检查参数,建立参数的边界值等等。 )。我很满意地理解如何context manager以及manually raise exceptions。
from numbers import Number
def foo(self, param1 = None, param2 = 0.0, param3 = 1.0):
if (param1 == None):
raise ValueError('This parameter is mandatory')
elif (not isinstance(param2, Number)):
raise ValueError('This parameter must be a valid Numerical value')
elif (param3 <= 0.0):
raise ValueError('This parameter must be a Positive Number')
...
这是Python中可接受的(经过验证的)参数检查方式,但我不得不怀疑:由于Python除了if-then-else语句之外没有编写Switch-case的方法,因此效率更高或正确的方法来执行此任务?或者正在实施if-then-else语句的长篇是我唯一的选择?
答案 0 :(得分:2)
您可以创建装饰器函数并将期望类型和(可选)范围作为参数传递。像这样:
def typecheck(types, ranges=None):
def __f(f):
def _f(*args, **kwargs):
for a, t in zip(args, types):
if not isinstance(a, t):
raise ValueError("Expected %s got %r" % (t, a))
for a, r in zip(args, ranges or []):
if r and not r[0] <= a <= r[1]:
raise ValueError("Should be in range %r: %r" % (r, a))
return f(*args, **kwargs)
return _f
return __f
而不是if ...: raise
您也可以反转条件并使用assert
,但作为noted in comments,这些可能并不总是被执行。
你也可以扩展它以允许例如开放范围(如(0., None)
)或接受任意(lambda
)函数以进行更具体的检查。
示例:
@typecheck(types=[int, float, str], ranges=[None, (0.0, 1.0), ("a", "f")])
def foo(x, y, z):
print("called foo with ", x, y, z)
foo(10, .5, "b") # called foo with 10 0.5 b
foo([1,2,3], .5, "b") # ValueError: Expected <class 'int'>, got [1, 2, 3]
foo(1, 2.,"e") # ValueError: Should be in range (0.0, 1.0): 2.0
答案 1 :(得分:1)
我认为您可以使用装饰器来检查参数。
def parameterChecker(input,output):
... def wrapper(f):
... assert len(input) == f.func_code.co_argcount
... def newfun(*args, **kwds):
... for (a, t) in zip(args, input):
... assert isinstance(a, t), "arg {} need to match {}".format(a,t)
... res = f(*args, **kwds)
... if not isinstance(res,collections.Iterable):
... res = [res]
... for (r, t) in zip(res, output):
... assert isinstance(r, t), "output {} need to match {}".format(r,t)
... return f(*args, **kwds)
... newfun.func_name = f.func_name
... return newfun
... return wrapper
example:
@parameterChecker((int,int),(int,))
... def func(arg1, arg2):
... return '1'
func(1,2)
AssertionError: output 1 need to match <type 'int'>
func(1,'e')
AssertionError: arg e need to match <type 'int'>
答案 2 :(得分:0)
这一直困扰着我一段时间关于Python,如果提供的param为None或缺少值,则没有标准的输出方法,也没有优雅地处理JSON / Dict对象,
例如我想在错误消息中输出实际参数名称
username = None
if not username:
log.error("some parameter is missing value")
没有办法传递实际的参数名称,除非你通过在错误输出消息中对参数进行硬编码来人为地弄乱,即
if not username:
log.error("username is missing value")
但这既麻烦又容易出现语法错误,并且难以维持。
出于这个原因,我写了一个“独裁者”功能,
https://medium.com/@mike.reider/python-dictionaries-get-nested-value-the-sane-way-4052ab99356b
如果将参数添加到dict中,或者从YAML或JSON配置文件中读取参数,如果param为null,则可以告诉Dictator引发ValueError,
例如,
config.yaml
skills:
sports:
- hockey
- baseball
现在你的py程序读入这个配置文件和参数,作为JSON字典,
with open(conf_file, 'r') as f:
config = yaml.load(f)
现在设置你的参数,并检查它们是否为NULL
sports = dictator(config, "skills.sports", checknone=True)
如果sports为None,则会引发ValueError,告诉您确切缺少哪个参数
ValueError("missing value for ['skills']['sports']")
你也可以为你的参数提供一个后备值,所以如果它是None,给它一个默认的后备值,
sports = dictator(config, "skills.sports", default="No sports found")
这可以避免丑陋的Index / Value / Key错误异常。
它是一种灵活,优雅的方式来处理大型字典数据结构,还使您能够检查程序的Null值参数并在错误消息中输出实际参数名称