在函数中引发信号表明未满足前置条件的适当例外是什么?
示例:
def print_stats(name, age):
if name is None:
raise Exception("name cannot be None")
if not type(name) is str:
raise Exception("name must be a string")
if age is None:
raise Exception("age cannot be None")
if age < 0:
raise Exception("age cannot be negative")
print("{0} is {1} years old".format(name, age))
答案 0 :(得分:4)
您应该同时使用TypeError
和ValueError
。
前三个例外应该是TypeError
,因为我们发信号通知参数类型不正确。来自docs:
例外
TypeError
将操作或函数应用于的对象时引发 不合适的类型。关联值是一个提供详细信息的字符串 关于类型不匹配。
然而,最后一个异常应该是ValueError
,因为age
是正确的类型,但值不正确(它是负数)。来自docs:
例外
ValueError
当内置操作或函数接收到参数时引发 具有正确的类型但价值不合适,情况如此 没有被更精确的例外描述,例如
IndexError
。
答案 1 :(得分:3)
我还认为您应该使用TypeError
和ValueError
,但您也可以改进应用前提条件的方式。
前段时间我正在玩后置条件和先决条件。 Python允许您使用装饰器编写更优雅的解决方案,而不是函数内的if
语句。
例如:
def precondition(predicate, exception, msg): // 1
def wrapper(func):
def percond_mechanism(*args, **kwargs): // 2
if predicate(*args, **kwargs):
return func(*args, **kwargs) // 3
else:
raise exception(msg) // 4
return percond_mechanism
return wrapper
现在你可以这样编写你的函数:
@precondition(lambda name, age: name is not None, ValueError, "name can't be None")
@precondition(lambda name, age: type(name) is str, TypeError, "name has to be str")
# You can continue adding preconditions here.
def print_stats(name, age):
print("{0} is {1} years old".format(name, age))
这种方式更容易阅读可以做什么和不可以做什么。实际上,你可以在你想要的任何函数中使用这个precondition
装饰器。
答案 2 :(得分:0)
我会选择ValueError
:
当内置操作或函数接收到参数时引发 具有正确的类型但价值不合适,情况如此 没有由更准确的异常描述,例如IndexError。
答案 3 :(得分:0)
我喜欢Raydel Miranda's answer使用该函数的装饰器前置条件。这是一种有点类似的方法,而不是装饰器,使用内省和eval
。效率会降低,但可以说更简洁,更具表现力。
import inspect
class ValidationError(ValueError):
pass
def validate(assertion, exc=ValidationError, msg=''):
"""
Validate the given assertion using values
from the calling function or method. By default,
raises a `ValidationException`, but optionally
raises any other kind of exeception you like.
A message can be provided, and will be formatted
in the context of the calling function. If no
message is specified, the test assertion will be
recapitulated as the cause of the exception.
"""
frame = inspect.currentframe().f_back
f_locals, f_globals = frame.f_locals, frame.f_globals
result = eval(assertion, f_globals, f_locals)
if result:
return
else:
if msg:
msg = msg.format(**f_locals)
else:
msg = 'fails test {0!r}'.format(assertion)
raise(exc(msg))
def r(name):
validate('isinstance(name, str)', msg='name must be str (was {name!r})')
validate('name.strip() != ""', msg='name must be non-empty (was {name!r})')
print(name,)
def r2(name, age):
validate('isinstance(name, str)', TypeError, 'name must be str (was {name!r})')
validate('name.strip() != ""', ValueError, 'name must be non-empty (was {name!r})')
validate('isinstance(age, int)', TypeError, 'age must be int (was {age!r})')
validate('age >= 0', ValueError, 'age must be non-negative (was {age!r})')
print(name,)
r('Joe')
r('')
r2('Dale', -3)
r2('Dale', None)
这将引发例外情况:
ValidationError: name must be non-empty (was '')
同样不错:如果您没有指定任何消息,它仍会提供合理的输出。例如:
def r2simple(name, age):
validate('isinstance(name, str)')
validate('name.strip() != ""')
validate('isinstance(age, int)')
validate('age >= 0')
print(name,)
r2simple('Biff', -1)
收率:
ValidationError: fails test 'age >= 0'
这将在Python 2或3下运行。