未满足的前提条件的正确例外是什么?

时间:2014-11-13 18:17:41

标签: python exception conditional-statements

在函数中引发信号表明未满足前置条件的适当例外是什么?

示例:

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))

4 个答案:

答案 0 :(得分:4)

您应该同时使用TypeErrorValueError

前三个例外应该是TypeError,因为我们发信号通知参数类型不正确。来自docs

  

例外 TypeError

     

将操作或函数应用于的对象时引发   不合适的类型。关联值是一个提供详细信息的字符串   关于类型不匹配。

然而,最后一个异常应该是ValueError,因为age是正确的类型,但值不正确(它是负数)。来自docs

  

例外 ValueError

     

当内置操作或函数接收到参数时引发   具有正确的类型但价值不合适,情况如此   没有被更精确的例外描述,例如IndexError

答案 1 :(得分:3)

我还认为您应该使用TypeErrorValueError,但您也可以改进应用前提条件的方式。

前段时间我正在玩后置条件和先决条件。 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
  1. 条件,如果条件未满足则要引发的例外以及要显示的信息。
  2. 此部分检查条件是否已满足。
  3. 如果确实可以,只需返回原始功能的结果。
  4. 如果没有,请提出您传递的异常消息。
  5. 现在你可以这样编写你的函数:

    @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。

来源:https://docs.python.org/2/library/exceptions.html

答案 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下运行。