Python进阶与拾遗8:Python中的异常处理
- 异常相关概念
- 异常的定义
- 异常的角色
- 常用的异常处理方法
- try/except/else/finally语句
- raise语句
- assert语句
- with/as环境管理器
- 相关概念
- 环境管理协议
- 异常对象
- 写在最后
作为一门面向对象编程的语言,异常处理是Python中常用的技术。本篇博文主要讲解Python中的异常处理,下面开始干货。
异常相关概念
异常的定义
异常,是可以改变程序中流程的事件。可以根据错误自动地被触发,也可能由代码触发或截获。
异常的角色
- 错误处理。运行检测到程序错误时,会引发异常。
- 事件通知。用于发出有效状态的信号,对程序进行通知。
- 特殊情况的处理。用于处理很罕见的情况。
- 终止行为。在有必要时结束程序运算。
- 非常规控制流程。类似于go to语句。
常用的异常处理方法
try/except/else/finally语句
- try代码语句块内如果发生了异常,就执行第一个符合引发异常的except子句下面的语句。当except代码执行完毕后,会回到try继续执行。
- 如果try代码语句块内发生了异常,但是没有符合的except语句,异常会向上传递到更上层的try语句块,直到传递到这个进程的顶层。
- 如果try首行底下执行的语句没有发生异常,会执行else语句。
- except分句数目无限制,但是只有一个else。
- except语句没列出异常名称时,捕捉在try语句中的所有异常。
- except语句可使用括号列出出现的任何异常。
- 无论是否发生异常,都会执行finally语句。在异常发生时,执行finally语句后,程序会把异常向上传递到更上层的try语句块,直到传递到这个进程的顶层。
- 语句顺序为:try -> except -> else -> finally。如果出现else,必须至少有一个except。finally可有可无。
try语句分句形式 | 说明 |
except: | 捕捉所有异常类型 |
except name: | 只捕捉特定异常 |
except name as value: | 捕捉所列的异常和其额外的数据 |
except (name1, name2): | 捕捉列出的任何异常 |
except (name1, name2) as value: | 捕捉列出的任何异常,并取得其额外数据 |
else: | 如果没有引发异常,就运行 |
finally: | 总是会运行的代码块 |
def print_addition(a, b):
try:
print("try:")
print(a + b)
print("try end!")
except TypeError as value:
print("except TypeError:")
print("TypeError occurs")
print(value)
else:
print("else:")
print(a, b)
print("no error!")
finally:
print("finally:")
print("finally go!")
def main():
print_addition(1, 2)
print_addition([11, 22, 33], "abc")
if __name__ == "__main__":
main()
'''
输出:
try:
3
try end!
else:
1 2
no error!
finally:
finally go!
try:
except TypeError:
TypeError occurs
can only concatenate list (not "str") to list
finally:
finally go!
'''
raise语句
- 用于显示地触发异常。
- raise后面可以接实例,类或者什么都不接。传递一个类,会自动创建一个实例。如果什么都不接,那么就传递最近引发的异常。
def print_addition(a, b):
try:
print("try:")
print(a + b)
print("try end!")
except:
print("raise error!")
raise
else:
print("else:")
print(a, b)
print("no error!")
finally:
print("finally:")
print("finally go!")
def main():
print_addition(1, 2)
print_addition([11, 22, 33], "abc")
if __name__ == "__main__":
main()
'''
输出:
try:
3
try end!
else:
1 2
no error!
finally:
finally go!
try:
raise error!
finally:
finally go!
Traceback (most recent call last):
File "D:/Pycharm/MyProjects/pythonException/Raise.py", line 22, in <module>
main()
File "D:/Pycharm/MyProjects/pythonException/Raise.py", line 19, in main
print_addition([11, 22, 33], "abc")
File "D:/Pycharm/MyProjects/pythonException/Raise.py", line 4, in print_addition
print(a + b)
TypeError: can only concatenate list (not "str") to list
'''
- 在Python 3.0及之后的版本中,可以使用from。表示从一个异常引发另外的异常。
raise exception from otherexception
def raise_from():
try:
1 / 0
except Exception as E:
raise TypeError from E
def main():
raise_from()
if __name__ == "__main__":
main()
'''
输出:
Traceback (most recent call last):
File "D:/Pycharm/MyProjects/pythonException/Raise_2.py", line 3, in raise_from
1 / 0
ZeroDivisionError: division by zero
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "D:/Pycharm/MyProjects/pythonException/Raise_2.py", line 12, in <module>
main()
File "D:/Pycharm/MyProjects/pythonException/Raise_2.py", line 9, in main
raise_from()
File "D:/Pycharm/MyProjects/pythonException/Raise_2.py", line 5, in raise_from
raise TypeError from E
TypeError
'''
assert语句
assert <test>, <data>
如果test为假,就引发AssertionError,data是额外数据,一般用于传递异常发生的描述。
def assert_test():
assert 1 == 0, "AssertionError occurs!"
def main():
assert_test()
if __name__ == "__main__":
main()
'''
输出:
Traceback (most recent call last):
File "D:/Pycharm/MyProjects/pythonException/Assert.py", line 9, in <module>
main()
File "D:/Pycharm/MyProjects/pythonException/Assert.py", line 6, in main
assert_test()
File "D:/Pycharm/MyProjects/pythonException/Assert.py", line 2, in assert_test
assert 1 == 0, "AssertionError occurs!"
AssertionError: AssertionError occurs!
'''
with/as环境管理器
相关概念
- 这个功能在Python 2.6及之后的版本中,Python 3.0及之后的版本中新增。通常使用环境管理器强化一些内置工具,比如自行关闭文件,对锁的自动上锁与开锁等。
with expression [as variable]:
with-block
- expression返回一个对象,从而支持环境管理协议,variable是可选的,表示这个对象返回的一个值(注意不是expression返回的对象)。比如,用于文件读写,在with语句执行后,文件会自动关闭;在with中引发了异常,文件也会自动关闭。
with open('file_path') as file:
for line in file:
print(line)
环境管理协议
可以编写一个类,用特殊的方法接入with语句,规范如下:
- 必须有__enter__和__exit__方法。
- 在with语句中,环境管理器的__enter__方法会被调用,如果as语句存在,就会赋值给as子句中的变量,否则直接丢弃。
- 代码块中的嵌套代码会执行。
- 如果with代码块引发异常,__exit__(type, value, traceback)方法会调用。如果此方法返回值为假,那么异常会重新引发,否则异常会终止(一般来说都会返回假)。
- 如果with代码块没有引发异常,__exit__(type, value, traceback)方法依然会调用,三个参数都以None传递。
class TraceBlock:
def message(self, arg):
print('running', arg)
def __enter__(self):
print('starting with block')
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type is None:
print('exited normally\n')
else:
print('raise an exception!', exc_type)
return False
# return True
def main():
with TraceBlock() as action:
action.message('test 1')
print('reached')
with TraceBlock() as action:
action.message('test 2')
raise TypeError
print('not reached')
if __name__ == "__main__":
main()
'''
输出:
Traceback (most recent call last):
File "D:/Pycharm/MyProjects/pythonException/With_Exception.py", line 26, in <module>
main()
File "D:/Pycharm/MyProjects/pythonException/With_Exception.py", line 22, in main
raise TypeError
TypeError
starting with block
running test 1
reached
exited normally
starting with block
running test 2
raise an exception! <class 'TypeError'>
'''
class TraceBlock:
def message(self, arg):
print('running', arg)
def __enter__(self):
print('starting with block')
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type is None:
print('exited normally\n')
else:
print('raise an exception!', exc_type)
# return False
return True
def main():
with TraceBlock() as action:
action.message('test 1')
print('reached')
with TraceBlock() as action:
action.message('test 2')
raise TypeError
print('not reached')
if __name__ == "__main__":
main()
'''
输出:
starting with block
running test 1
reached
exited normally
starting with block
running test 2
raise an exception! <class 'TypeError'>
'''
异常对象
- Python 3.0及之后的版本要求异常类派生自BaseException内置异常超类,绝大多数程序都继承自Exception子类。Python 2.6及之后版本中的新式类规则相同。
- BaseException是异常的顶级根类,不能由用户直接继承。提供了子类所继承的默认的打印和状态保持行为。
- Exception是与应用异常相关的顶层根超类,是BaseException的一个直接子类,并且是所有内置异常的直接超类。在用户自定义异常时,会继承这个类。
- 内置异常提供了默认打印显示和状态保持,传递给这些类的任何参数都会保存在实例的args元组中。这就是为什么传递给内置异常类的参数会显示在出错消息中,当打印实例时,附加给实例的任何构造函数参数就会显示。对用户定义的异常类也如此,因为继承了内置超类中存在的构造函数和使用方法。
I = IndexError('abc')
I.args
raise IndexError('abc')
class E(Exception): pass
def main():
try:
raise E('abc', 'def')
except E as X:
print(X.args) # ('abc', 'def')
if __name__ == "__main__":
main()
- 在异常类中,还可以定制打印显示,重载__str__方法。注意,必须重载__str__,因为超类已经有这个方法了。不能重载__repr__,因为__str__优先级高于__repr__,还是会调用超类的__str__。
class E(Exception):
def __str__(self):
return 'Exception E occurs!'
def main():
try:
raise E('abc', 'def')
except E as X:
print(X.args)
print(X)
if __name__ == "__main__":
main()
'''
输入:
('abc', 'def')
Exception E occurs!
'''
- 定制数据与行为。可以通过构造函数和方法定义,为异常类进行定制。
class FormatError(Exception):
logfile = 'error_log.txt'
def __init__(self, line, file):
self.line = line
self.file = file
def logerror(self):
log = open(self.logfile, 'a+')
print('Error at', self.file, self.line, file=log)
def parser():
raise FormatError(40, 'error.txt')
def main():
try:
parser()
except FormatError as exc:
exc.logerror()
执行后,可见工程目录下新建了error.txt,其中内容如下:
Error at error.txt 40
写在最后
截止文篇博文,Python基础与拾遗和Python进阶与拾遗一共17讲的内容就告一段落了。在阅读完毕这两部分后,就可以结合Python语言独有的技巧,进行面向对象的Python工程编写了。这也是笔者对Python语言相关技术的总结与复盘,希望能在未来推出更多技术干货,与各位读者朋友共同成长。
欢迎阅读笔者后续博客,各位读者朋友的支持与鼓励是我最大的动力!
written by jiong
君问归期未有期,
巴山夜雨涨秋池。
何当共剪西窗烛,
却话巴山夜雨时。