错误和异常
##################################################################################
参考:http://blog.csdn.net/fcoolx/article/details/4202872
https://docs.python.org/2/library/exceptions.html#bltin-exceptions
##################################################################################
What?
什么是错误和异常?
错误是指在执行代码过程中发生的事件,它中断或干扰代码的正常流程并创建异常对象。当错误中断流程时,该程序将尝试寻找异常处理程序(一段告诉程序如何对错误做出响应的代码),以帮助程序恢复流程。换句话说,错误是一个事件,而异常是该事件创建的对象。
当使用短语“产生异常”时,表示存在问题的方法发生错误,并创建异常对象(包含该错误的信息及发生的时间和位置)来响应该错误。导致出现错误和随后异常的因素包括用户错误、资源失败和编程逻辑失败。这些错误与代码实现特定任务的方法有关,而与该任务的目的无关。
why?
异常怎么发生的?
异常的抛出机制:
1、如果在运行时发生异常,解释器会查找相应的处理语句(称为handler).
2、要是在当前函数里没有找到的话,它会将异常传递给上层的调用函数,看看那里能不能处理。
3、如果在最外层(全局“main”)还是没有找到的话,解释器就会退出,同时打印出traceback以便让用户找到错误产生的原因。
注意:虽然大多数错误会导致异常,但一个异常不一定代表错误,有时候它们只是一个警告,有时候它们可能是一个终止信号,比如退出循环等。
为什么要处理异常?
如果不进行异常处理,即不对错误做出响应,程序的健壮性就会大打折扣,甚至无法保证正常运行,所以必须要进行异常处理。
异常类型有哪些?
异常类型参考:https://docs.python.org/2/library/exceptions.html#bltin-exceptions
基础异常类型
BaseException #所有异常的基类
+-- SystemExit #python 解释器请求退出
+-- KeyboardInterrupt #用户中断执行(通常是输入^C)
+-- GeneratorExit #生成器(generator)发生异常来通知退出
+-- Exception #常规错误的基类
+-- StopIteration #迭代器没有更多的值
+-- StandardError #所有的内建标准异常的基类
| +--BufferError| +-- ArithmeticError #所有数值计算错误的基类
| | +-- FloatingPointError #浮点计算错误
| | +-- OverflowError #数值运算超出最大限制
| | +-- ZeroDivisionError #除(或取模)零 (所有数据类型)
| +-- AssertionError #断言语句失败
| +-- AttributeError #对象没有这个属性
| +-- EnvironmentError #操作系统错误的基类
| | +-- IOError #输入输出错误
| | +-- OSError #操作系统错误
| | +-- WindowsError (Windows) #Windows 系统调用失败
| | +--VMSError (VMS)| +-- EOFError #没有内建输入,到达EOF 标记
| +-- ImportError #导入模块/对象失败
| +-- LookupError #无效数据查询的基类
| | +-- IndexError #序列中没有没有此索引(index)
| | +-- KeyError #映射中没有这个键
| +-- MemoryError #内存溢出错误(对于Python 解释器不是致命的)
| +-- NameError #未声明/初始化对象 (没有属性)
| | +-- UnboundLocalError #访问未初始化的本地变量
| +-- ReferenceError #弱引用(Weak reference)试图访问已经垃圾回收了的对象
| +-- RuntimeError #一般的运行时错误
| | +-- NotImplementedError#尚未实现的方法
| +-- SyntaxError #Python 语法错误
| | +-- IndentationError #缩进错误
| | +-- TabError #Tab 和空格混用
| +-- SystemError #一般的解释器系统错误
| +-- TypeError #对类型无效的操作
| +-- ValueError #传入无效的参数
| +-- UnicodeError #Unicode 相关的错误
| +-- UnicodeDecodeError #Unicode 解码时的错误
| +-- UnicodeEncodeError #Unicode 编码时错误
| +-- UnicodeTranslateError #Unicode 转换时错误
+-- Warning #警告的基类
+-- DeprecationWarning #关于被弃用的特征的警告
+-- PendingDeprecationWarning #关于特性将会被废弃的警告
+-- RuntimeWarning #可疑的运行时行为(runtime behavior)的警告
+-- SyntaxWarning #可疑的语法的警告
+-- UserWarning #用户代码生成的警告
+-- FutureWarning #关于构造将来语义会有改变的警告
+--ImportWarning+--UnicodeWarning+-- BytesWarning
1、NameError:尝试访问一个未申明的变量>>>v
NameError: name'v' is notdefined2、ZeroDivisionError:除数为0>>> v = 1/0
ZeroDivisionError: int divisionormodulo by zero3、SyntaxError:语法错误>>>int int
SyntaxError: invalid syntax (, line 1)
4、IndexError:索引超出范围>>> List = [2]>>> List[3]
Traceback (most recent call last):
File"", line 1, in List[3]
IndexError: list index out of range5、KeyError:字典关键字不存在>>> Dic = {'1':'yes', '2':'no'}>>> Dic['3']
Traceback (most recent call last):
File"", line 1, in Dic['3']
KeyError:'3'
6、IOError:输入输出错误>>> f = open('abc')
IOError: [Errno2] No such file or directory: 'abc'
7、AttributeError:访问未知对象属性>>> classWorker:defWork():print("I am working")>>> w =Worker()>>>w.a
Traceback (most recent call last):
File"", line 1, in w.a
AttributeError:'Worker' object has no attribute 'a'
8、ValueError:数值错误>>> int('d')
Traceback (most recent call last):
File"", line 1, in int('d')
ValueError: invalid literalfor int() with base 10: 'd'
9、TypeError:类型错误>>> iStr = '22'
>>> iVal = 22
>>> obj = iStr +iVal;
Traceback (most recent call last):
File"", line 1, in obj= iStr +iVal;
TypeError: Can't convert'int'object to str implicitly
10、AssertionError:断言错误>>> assert 1 != 1Traceback (most recent call last):
File"", line 1, in
assert 1 != 1AssertionError
如何处理异常?
使用try—except 等语句来处理选定异常,例如
>>> whileTrue:
...try:
... x= int(raw_input("Please enter a number:"))
...break...exceptValueError:
...print "Oops! That was no valid number. Try again..."...
try语句按如下方式工作
l 首先,try从句(在关键字try和except之间的语句)被执行。
l 如果没有异常发生,except从句被跳过,try语句执行完毕。
l 如果在执行try语句的时候有异常发生,剩余的语句会被跳过。如果它的类型和except后面的命名异常相匹配,这个except从句被执行,然后在try语句后面继续执行。
l 如果有异常发生,并且它和except从句中的命名异常类型不匹配,它被传递到外层的try语句,如果没有发现处理程序,它就是一个未处理的异常,程序停止执行,显示上面的错误信息。
一个try语句或许有多个except从句,来为不同的异常指定处理程序。至多一个处理程序将被执行。处理程序只处理发生在相应try从句中的异常,而不处理发生在同一个try语句的其它处理程序中的异常。一个except从句可以命名多个异常,使用一个括号元组,例如:
... except(RuntimeError, TypeError, NameError):
...pass
最后一个except从句或许可以忽略异常的名字,作为通配符。这样使用应该极其谨慎,因为这样容易掩饰掉一个真正的编程错误。它也可以被用来打印一个错误信息,然后重新引发这个异常(也允许调用者来处理这个异常):
importsystry:
f= open('myfile.txt')
s=f.readline()
i=int(s.strip())exceptIOError as e:print "I/O error({0}): {1}".format(e.errno, e.strerror)exceptValueError:print "Could not convert data to an integer."
except:print "Unexpected error:", sys.exc_info()[0]raise
try...except语句有一个可选的else从句,如果有的话必须跟在所有的except从句后面。对于那些异常没有发生时必须执行的代码非常有用。例如:
for arg in sys.argv[1:]:try:
f= open(arg, 'r')exceptIOError:print 'cannot open', argelse:print arg, 'has', len(f.readlines()), 'lines'f.close()
使用 else 子句比在 try 子句中附加代码要好,因为这样可以避免 try ... except 意外的截获本来不属于它们保护的那些代码抛出的异常。
Finally子句,无论异常是否发生,是否捕捉都会执行的一段代码。
下面是try-except-else-finally 语法的示例:
try:
AexceptMyException: Belse: Cfinally: D #无论如何都执行
>>> defdivide(x, y):
...try:
... result= x /y
...exceptZeroDivisionError:
...print "division by zero!"...else:
...print "result is", result
...finally:
...print "executing finally clause"...>>> divide(2, 1)
resultis 2executingfinallyclause>>> divide(2, 0)
division by zero!
executingfinallyclause>>> divide("2", "1")
executingfinallyclause
Traceback (most recent call last):
File"", line 1, in?
File"", line 3, individe
TypeError: unsupported operand type(s)for /: 'str' and 'str'
finally 子句在任何情况下都会执行。 TypeError 在两个字符串相除的时候抛出,未被 except 子句捕获,因此在 finally 子句执行完毕后重新抛出。
try-except 和try-finally 包含的方式
ccfile =Nonetry:try:
ccfile= open('carddata.txt', 'r')
txns=ccfile.readlines()finally:ifccfile:
ccfile.close()exceptIOError:
log.write('no txns this month\n')
一个这样写的理由是如果在finally 的语句块内发生了一个异常,你可以创建一个同现有的异常处理器在同一个(外)层次的异常处理器来处理它.这样,从本质上来说,就可以同时处理在原始的try语句块和finally 语句块中发生的错误.这种方法唯一的问题是,当finally 语句块中的确发生异常时,你会丢失原来异常的上下文信息,除非你在某个地方保存了它.
反对这种写法的一个理由是:在很多情况下,异常处理器需要做一些扫尾工作,而如果你在异常处理之前,用finally 语句块中释放了某些资源,你就不能再去做这项工作了.简单的说,finally 语句块并不是如你所想的是"最终的(final)"了.
一个最终的注意点:如果finally 中的代码引发了另一个异常或由于return,break,continue 语法而终止,原来的异常将丢失而且无法重新引发.
若要捕获所有异常,则可以:
try:
:exceptException, e:#error occurred, log 'e', etc
因为Exception是在异常继承树结构的最顶层,异常的捕获都是向下包容的,比如except LookupError,这样LookupError下的IndexError和KeyErroe异常就都能被捕获
异常参数
当异常被引发后参数是作为附加帮助信息传递给异常处理器的,异常原因是可选。
#single exception
exceptException[, reason]:
suite_for_Exception_with_Argument#multiple exceptions
except(Exception1, Exception2, ..., ExceptionN)[, reason]:
suite_for_Exception1_to_ExceptionN_with_Argument
reason 将会是一个包含来自导致异常的代码的诊断信息的类实例. 异常参数自身会组成一个元组,并存储为类实例(异常类的实例)的属性. 上边的第一种用法中, reason 将会是一个Exception 类的实例.
>>> whileTrue:try:
x= int(raw_input('please enter a number:'))break
except ValueError, e(or, exceptValueError as e):print 'ValueError'please enter a number:a
ValueError
please enter a number:5
>>>type(e)
>>> printe
invalid literalfor int() with base 10: 'a'
>>>
我们首先在一个try 语句块中引发一个异常,随后简单的忽略了这个异常,但保留了错误的信息。调用内置的type()函数,我们可以确认我们的异常对象的确是ValueError 异常类的实例。最后我们对异常诊断参数调用print 以显示错误。
综合了这一章目前我们所见过的所有不同的可以处理异常的语法样式:
try:
try_suiteexceptException1:
suite_for_Exception1except(Exception2, Exception3, Exception4):
suite_for_Exceptions_2_3_and_4exceptException5, Argument5:
suite_for_Exception5_plus_argumentexcept(Exception6, Exception7), Argument67:
suite_for_Exceptions6_and_7_plus_argumentexcept:
suite_for_all_other_exceptionselse:
no_exceptions_detected_suitefinally:
always_execute_suite
触发异常
--raise语句
rasie 一般的用法是:
raise [SomeException [, args [, traceback]]]
第一个参数,SomeExcpetion,是触发异常的名字.如果有,它必须是一个字符串,类或实例
第二个符号为可选的args(比如参数,值),来传给异常.
第三个参数traceback,同样是可选的(实际上很少用它),
raise 语句允许程序员强制抛出一个指定的异常。例如:
>>> raise NameError('HiThere')
Traceback (most recent call last):
File"", line 1, in?
NameError: HiThere
如果你需要明确一个异常是否抛出,但不想处理它, raise 语句可以让你很简单的重新抛出该异常:
>>> try:
...raise NameError('HiThere')
...exceptNameError:
...print 'An exception flew by!'...raise...
An exception flew by!
Traceback (most recent call last):
File"", line 2, in?
NameError: HiThere
用户自定义异常
在程序中可以通过创建新的异常类型来命名自己的异常(Python 类的内容请参见 类 )。异常类通常应该直接或间接的从 Exception 类派生,例如:
>>> classMyError(Exception):
...def __init__(self, value):
... self.value=value
...def __str__(self):
...returnrepr(self.value)
...>>> try:
...raise MyError(2*2)
...exceptMyError as e:
...print 'My exception occurred, value:', e.value
...
My exception occurred, value:4
>>> raise MyError('oops!')
Traceback (most recent call last):
File"", line 1, in?__main__.MyError: 'oops!