2014-11-13 14 views
1

什么是适当的例外,以函数形式表示没有满足前提条件?未满足前提条件的正确例外是什么?

例子:

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

你应该同时使用TypeErrorValueError

前三个例外应该是TypeError s,因为我们发信号表明参数的类型不正确。从docs

例外TypeError

当施加到 不合适的类型的对象的操作或功能的高位。关联的值是一个字符串,提供有关类型不匹配的详细信息 。

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

例外ValueError

长大当一个内置操作或功能收到 有正确的类型,但不恰当的值,情况是不是说明 参数通过更精确的例外,如IndexError

+0

出于好奇,一个'TypeError'适用于'的操作或功能被应用到不适当类型的对象“。严格地说 - 在执行操作(“让它失败”)时将它视为TypeError,还是将其视为验证错误(ValueError)?我在看“操作或函数接收参数”与“操作或函数应用于对象”之间的短语,如果有意义的话。 (据我所知,在这个特定的例子中None是错误的类型。) –

+1

'ValueError'并不完全是“验证错误”。这将更像是一个'AssertionError'。 “TypeError”和“ValueError”之间的区别仅仅是“不恰当的对象是否具有正确的类型?”这个问题。如果是这样,引发一个'ValueError'。否则,引发一个'TypeError'。除此之外,两个例外基本上是相同的;他们都意味着我们遇到了不适当的对象。 – iCodez

2

我也认为你应该使用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. 如果没有,请提出您的消息通过的例外。

现在你可以写你的函数是这样的:

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

这种方式是更容易阅读什么可以和什么不能做。而实际上,你可以在你想要的任何功能中使用这个装饰器。

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.