1.什么是异常(what)
异常:不正常的情况
不正常的情况,在程序中,会有两种体现:
l自己造孽:写错代码了!错误也是一种异常;程序都运行不起来!
l程序运行过程中,在某些特定条件下,不合适的数据引起程序出现错误导致程序崩溃
n要求用户输入一个数字,但是用户误操作输入了字符串,在进行类型转换时就会出现错误导致程序崩溃!
why为什么要处理异常:
当程序在运行过程中,由于用户的误操作或者不合适的数据引发的程序错误,让代码自己处理并保证程序的正常执行。而不至于因为错误导致程序崩溃!
提高代码的健壮性!
def div(a, b):
try:
print(a / b)
except ZeroDivisionError:
print("Error: b should not be 0 !!")
except Exception as e:
print("Unexpected Error: {}".format(e))
else:
print('Run into else only when everything goes well')
finally:
print('Always run into finally block.')
# tests
div(2, 0)
div(2, 'bad type')
div(1, 2)
# Mutiple exception in one line
try:
print(a / b)
except (ZeroDivisionError, TypeError) as e:
print(e)
# Except block is optional when there is finally
try:
open(database)
finally:
close(database)
# catch all errors and log it
try:
do_work()
except:
# get detail from logging module
logging.exception('Exception caught!')
# get detail from sys.exc_info() method
error_type, error_value, trace_back = sys.exc_info()
print(error_value)
Raise
总结如下:
except语句不是必须的,finally语句也不是必须的,但是二者必须要有一个,否则就没有try的意义了。
except语句可以有多个,Python会按except语句的顺序依次匹配你指定的异常,如果异常已经处理就不会再进入后面的except语句。
except语句可以以元组形式同时指定多个异常,参见实例代码。
except语句后面如果不指定异常类型,则默认捕获所有异常,你可以通过logging或者sys模块获取当前异常。
如果要捕获异常后要重复抛出,请使用raise,后面不要带任何参数或信息。
不建议捕获并抛出同一个异常,请考虑重构你的代码。
不建议在不清楚逻辑的情况下捕获所有异常,有可能你隐藏了很严重的问题。
尽量使用内置的异常处理语句来替换try/except语句,比如with语句,getattr()方法。
如果你需要自主抛出异常一个异常,可以使用raise关键字,等同于C#和Java中的throw语句,其语法规则如下。
raise NameError("bad name!")
raise关键字后面需要指定你抛出的异常类型,一般来说抛出的异常越详细越好,Python在exceptions模块内建了很多的异常类型,通过使用dir()函数来查看exceptions中的异常类型,如下:
import exceptions
# ['ArithmeticError', 'AssertionError'.....]
print dir(exceptions)
自定义异常类型
Python中也可以自定义自己的特殊类型的异常,只需要要从Exception类继承(直接或间接)即可:
class SomeCustomException(Exception):
pass
一般你在自定义异常类型时,需要考虑的问题应该是这个异常所应用的场景。如果内置异常已经包括了你需要的异常,建议考虑使用内置的异常类型。比如你希望在函数参数错误时抛出一个异常,你可能并不需要定义一个InvalidArgumentError,使用内置的ValueError即可。
经验案例
传递异常re-raise Exception
捕捉到了异常,但是又想重新引发它(传递异常),使用不带参数的raise语句即可:
def f1():
print(1/0)
def f2():
try:
f1()
except Exception as e:
raise # don't raise e !!!
f2()
在Python2中,为了保持异常的完整信息,那么你捕获后再次抛出时千万不能在raise后面加上异常对象,否则你的trace信息就会从此处截断。以上是最简单的重新抛出异常的做法。
还有一些技巧可以考虑,比如抛出异常前对异常的信息进行更新。
def f2():
try:
f1()
except Exception as e:
e.args += ('more info',)
raise
如果你有兴趣了解更多,建议阅读这篇博客。
http://www.ianbicking.org/blo...
使用内置的语法范式代替try/except
Python 本身提供了很多的语法范式简化了异常的处理,比如for语句就处理的StopIteration异常,让你很流畅地写出一个循环。
with语句在打开文件后会自动调用finally中的关闭文件操作。我们在写Python代码时应该尽量避免在遇到这种情况时还使用try/except/finally的思维来处理。
# should not
try:
f = open(a_file)
do_something(f)
finally:
f.close()
# should
with open(a_file) as f:
do_something(f)
再比如,当我们需要访问一个不确定的属性时,有可能你会写出这样的代码:
try:
test = Test()
name = test.name # not sure if we can get its name
except AttributeError:
name = 'default'
其实你可以使用更简单的getattr()来达到你的目的。
name = getattr(test, 'name', 'default')
最佳实践
最佳实践不限于编程语言,只是一些规则和填坑后的收获。
只处理你知道的异常,避免捕获所有 异常然后吞掉它们。
抛出的异常应该说明原因,有时候你知道异常类型也猜不出所以然的。
避免在catch语句块中干一些没意义的事情。
不要使用异常来控制流程,那样你的程序会无比难懂和难维护。
如果有需要,切记使用finally来释放资源。
如果有需要,请不要忘记在处理异常后做清理工作或者回滚操作。