当前位置: 首页 > 图文教程 > 服务器 > Linux服务器 > 服务器诊所:编写出色的异常
try:
process(some_file)
except:
alert("errorinopening"'%s'%some_file)
发现问题没有?异常的语法和语义不匹配,这有点类似于一个公务员,在向选民承诺要注意他们最关心的问题,尤其是游泳池开放时间。尽管这样的语句形式上正确,其失衡却会使听众感到震惊,暗示有更深层的问题。
上面这个不匹配的捕获也存在类似问题:它捕获了所有错误,但仅仅只报告了“打开文件时出现错误(errorinopening)”。这样写会好一点:
清单2.均衡性较好的捕获
try:
process(some_file)
exceptIOError:
alert("errorinopening"'%s'%some_file)
许多程序员由衷地认为这两个例子是等价的,因为一种可能的理由是,“记录process只是用来生成IOError”。在这层意义上讲,应用程序在这两个示例中的执行,的确毫无差别。可是源代码不只是给计算机用的;更重要的是必须向身为人类的读者表达其含意。如果您的代码假设某个特定的异常一定是一个IOError,那么利用该语言的精确性,就这么说。
第二个示例仍不能完全防止让最终用户看到“原始”异常的危险。实际上,即使process()在当前版本中被明白无误地记录为仅抛出IOError,但我仍要求在编写该段代码时至少达到下面这样的详细程度:
清单3.形式均衡且全面的捕获
try:
process(some_file)
exceptIOError:
alert("errorinopening"'%s'%some_file)
except:
alert("internalandcompletelyunexpectedproblem")
当然,对我们而言,拥有完整且正确记录的接口是一种少有的奢侈。在开发工作中许多语言采用了一种有用的技术-使用异常系统内置的内省。这使我们的示例变成这样:
清单4.形式均衡且全面的捕获,并带有信息性的“缺省设置”
try:
process(some_file)
exceptIOError:
alert("errorinopening"'%s'%some_file)
except:
(exc_class,exc_object,exc_traceback)=sys.exc_info()
alert("""internalandcompletelyunexpectedproblem,
manifestedas%s"""%str(exc_class))
举例来说,如果process()的实现产生了一个导致ValueError而不是IOError的错误,上面最后一个处理程序至少会将ValueError作为类名报告上来。
在捕获时还常有另一种含糊不清之处。其代码像这样:
错误处理中过宽的作用域
try:
first_operation()
second_operation()
third_operation()
fourth_operation()
except:
alert("somethingwentwrong")
这里的不足之处在于“横向”的不精确;当出现somethingwentwrong时,没有能立即与引发错误的特定*_operation()连接。一种简单的解决方案是一次只捕获一段,这样前面的编码就变成:
清单6.错误处理中更高的精确度
#Thedocumentationassuresusthese
#twocan'ttossexceptions.
first_operation()
second_operation()
try:
third_operation()
except:
alert("somethingwentwrongin'third_operation()'")
#This,also,cannotthrowanexception.
fourth_operation()
稍微复杂一点的解决方案是让捕获代码的范围更宽一些,但要使用语言的内省能力来报告追溯信息:
清单7.许多语言能管理自己的追溯
try:
first_operation()
second_operation()
third_operation()
fourth_operation()
except:
exc_traceback=sys.exc_info()[2]
stack_list=[]
while1:
stack_list.append(exc_traceback.tb_frame.f_code.co_name)
ifnotexc_traceback.tb_next:
break
exc_traceback=exc_traceback.tb_next
#Thenextisanalmost-human-readable
#descriptionofwherethefaultoccurred
alert("somethingwentwrongin%s"%stack_list)
于是,在捕获异常时,确信获取了全部异常且其处理方式是精确的,并使用您所选择的语言中的可用信息合成出有用的输出。
抛出
同异常的使用相比,生成异常是一个稍许高级的主题。但是,所有服务器诊所的读者都应知道抛出异常的基础:
记录接口。
保持简单的继承层次结构。
继承是由语言支持的用于异常值的出色技术;按IOError、ValueError、AppError等类别组织错误是一种相当有用的方法。然而,没有经验的设计人员常将他们的继承层次结构复杂化,以至于同最优的层次结构相差甚大。如果您发现在异常类中定义了两层以上的继承级别,那么就要检查一下。如果有三层以上,或者在一个异常超类中子类的个数超过了七个,那我打赌一定有什么地方出错了。
在这方面经常犯的一个错误是在Exception或者Error下复制了一棵应用程序对象树。这几乎总是是个错误,但是可以通过将异常参数化而不是子类化来简单地修正这个错误。一个指定了MercuryException和VenusException等的设计可能不是最好的;而用PlanetException进行编码,并附有数据指出哪一个planet是问题所在的设计可能会更好。
有一种稍许微妙的风格,可支持契约式设计(design-by-contact,DbC)方法,即在AssertionError之外
评论 (0) All