一. 什么是异常
python用异常对象(expection object)来表示异常情况。遇到错误后,会引发异常。如果是异常对象并未被处理或捕获,程序就会用所谓的回溯(Traceback,一种错误信息)终止执行:
>>> 1/0 Traceback (most recent call last): File "", line 1, in 1/0 ZeroDivisionError: division by zero
事实上,每个异常都是一些类(本例中是ZeroDivisionError)的实例,这些实例可以被引发,并且可以用很多种方法进行捕捉,使得程序可以捉住错误并对其进行处理,而不是让整个程序失败
二. 异常处理:try...except...(else)
以下例子中,让用户输入一个合法的整数,但允许用户中断整个程序(使用ctrl+c 或操作系统提供的方法),用户中断的信息会引发一个KeyboardInterrupt异常
while True: try: x = int(input("Please enter a number: ")) break except ValueError: print("Oops! That was no valid number. Try again!")
try语句按照如下方式工作:
-
首先,执行try子句(在关键字try和关键字except之间的语句)
-
如果没有异常发生,忽略except子句,try语句执行后结束
-
如果在执行try子句过程中发生了异常,那么try子句余下的部分将被忽略。如果异常的类型和except之后的名称相符,那么对应的except子句将被执行。最后执行try语句之后的代码
-
如果一个异常没有与任何的except匹配,那么这个异常将会传递给上层的try中
一个try语句可能包含多个except子句,分别来处理不同的特定的异常,最多只有一个分支会被执行
try except语句还有一个可选的else子句,如果使用这个子句,那么必须放在所有的except子句之后。这个子句在try子句没有发生任何异常的时候执行
#Filename: try_except.py
try: text = input("Enter something --> ") except EOFError: print("Why did you do an EOF on me?") except KeyboardInterrupt: print("You cancelled the operation.") else: print("You entered {0}".format(text))
输出
Enter something --> Press ctrl-d Why did you do an EOF on me? Enter something --> Press ctrl-c Why did you do an EOF on me? Enter something --> no exceptions You entered no exceptions
最后一个except子句可以忽略异常的名字,它将被当作通配符使用
import sys try: f = open("myfile.txt") s = f.readline() i = int(s.strip()) except OSError as err: print("OS error: {0}".format(err)) except ValueError: print("Could not convert data to an integer.") except: print("Unexcepted error:", sys.exc_info()[0]) raise
三. 抛出异常
可以使用raise抛出一个指定的异常。raise的参数是指定的要抛出的异常,它必须是一个异常的实例或异常的类(也就是Exception的子类)
#抛出了一个没有任何错误信息的异常 >>> raise Exception Traceback (most recent call last): File "", line 1, in raise Exception Exception #抛出了一个添加了错误信息的异常 >>> raise Exception("hyperdrive overload") Traceback (most recent call last): File " ", line 1, in raise Exception("hyperdrive overload") Exception: hyperdrive overload
四. 异常后的清理 try...(except)...finally
finally子句可以用来在可能的异常后进行清理,它和try子句联合使用:
x = None try: x = 1/0 finally: print("Cleaning up...") del x
上面的代码中,finally子句肯定会被执行,不管try子句中是否发生异常(在try子句之前初始化x的原因是:如果不这样做,由于ZeroDivisionError的存在,x就永远不会被赋值。这样就会导致在finally子句使用del删除它的时候产生异常,而且这个异常是无法捕捉的)
运行这段代码,在程序奔溃之前,对于变量x的清理就完成了:
clean up... Traceback (most recent call last): File "D:/python_workshop/python6/revise/try_except.py", line 16, inx = 1/0 ZeroDivisionError: division by zero
因为使用del语句删除一个变量是非常不负责的清理手段,所以finally子句用于关闭文件或者网络套接字时会非常有用,还可以在同一条语句中组合使用try, except, else, finally
try: 1/0 except NameError: print("Unknow variable") else: print("That went well!") finally: print("Cleaning up.")
五. 异常处理应用场景
1. 内部代码异常抛出
2. 应用于与外部资源进行交互的情况
1) 与计算机文件交互
2) 与数据库进行交互
3) 与外部进行http通信
六. with语句
在try块中获得资源,随后又在finally块中释放资源,这是一种常见的模式。也有一种with语句能够以清晰的方式完成这样的功能
with open能够在结束的时候自动关闭文件。在屏幕后面发生的事情就是with语句使用了一种协议,获得了open语句返回的对象,就叫"thefile"好了
在启动代码之前,在后台总会调用 thefile.__enter__函数,在代码结束后又会调用thefile.__exit__函数
with open("poem.txt") as f: for line in f: print(line, end="")
如果我们在poem.txt文件内写入python之禅(可通过import this查看),得到的结果是:
The Zen of Python, by Tim Peters Beautiful is better than ugly. Explicit is better than implicit. Simple is better than complex. Complex is better than complicated. Flat is better than nested. Sparse is better than dense. Readability counts. Special cases aren't special enough to break the rules. Although practicality beats purity. Errors should never pass silently. Unless explicitly silenced. In the face of ambiguity, refuse the temptation to guess. There should be one-- and preferably only one --obvious way to do it. Although that way may not be obvious at first unless you're Dutch. Now is better than never. Although never is often better than *right* now. If the implementation is hard to explain, it's a bad idea. If the implementation is easy to explain, it may be a good idea. Namespaces are one honking great idea -- let's do more of those!
参考文章:
《Python3 错误和异常》
《Python基础教程(第2版 修订版)》
《A Byte of Python3(中文版)》