💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
[TOC] # 捕获异常`try...except...` 看如下示例: ~~~ try: print('-----test--1---') open('123.txt','r') print('-----test--2---') except IOError: pass ~~~ * 此程序看不到任何错误,因为用except 捕获到了IOError异常,并添加了处理的方法 * pass 表示实现了相应的实现,但什么也不做;如果把pass改为print语句,那么就会输出其他信息 # except捕获多个异常 ~~~ #coding=utf-8 try: print('-----test--1---') open('123.txt','r') # 如果123.txt文件不存在,那么会产生 IOError 异常 print('-----test--2---') print(num)# 如果num变量没有定义,那么会产生 NameError 异常 except (IOError,NameError): #如果想通过一次except捕获到多个异常可以用一个元组的方式 # errorMsg里会保存捕获到的错误信息 print(errorMsg) ~~~ 当捕获多个异常时,可以把要捕获的异常的名字,放到except 后,并使用元组的方式仅进行存储 # 获取异常的信息描述 ![](https://box.kancloud.cn/c0e613698b504dc4dc924ab49459715c_671x166.png) ![](https://box.kancloud.cn/ec9f333dbc396458262378f2468ff93c_740x157.png) # 捕获所有异常 ![](https://box.kancloud.cn/1e73bbf23437b418f0fdddcffd0455c5_681x139.png) ![](https://box.kancloud.cn/b18f128c2e30eb1a84754d64d712040f_704x180.png) # else 咱们应该对else并不陌生,在if中,它的作用是当条件不满足时执行的实行; 同样在`try...except..`.中也是如此,即如果没有捕获到异常,那么就执行else中的事情 ![](https://box.kancloud.cn/75b189d130cb2220c1e1781365a817f5_642x219.png) # `try...finally...` try...finally...语句用来表达这样的情况: 在程序中,如果一个段代码必须要执行,即无论异常是否产生都要执行,那么此时就需要使用finally。 比如文件关闭,释放锁,把数据库连接返还给连接池等 ~~~ import time try: f = open('test.txt') try: while True: content = f.readline() if len(content) == 0: break time.sleep(2) print(content) except: #如果在读取文件的过程中,产生了异常,那么就会捕获到 #比如 按下了 ctrl+c pass finally: f.close() print('关闭文件') except: print("没有这个文件") ~~~ # 抛出自定义异常 可以用raise语句来引发一个异常。异常/错误对象必须有一个名字,且它们应是Error或Exception类的子类 下面是一个引发异常的例子: ~~~ class ShortInputException(Exception): '''自定义的异常类''' def __init__(self, length, atleast): #super().__init__() self.length = length self.atleast = atleast def main(): try: s = input('请输入 --> ') if len(s) < 3: # raise引发一个你定义的异常 raise ShortInputException(len(s), 3) except ShortInputException as result:#x这个变量被绑定到了错误的实例 print('ShortInputException: 输入的长度是 %d,长度至少应是 %d'% (result.length, result.atleast)) else: print('没有异常发生.') main() ~~~ ![](https://box.kancloud.cn/656014e329d6b123459465febdbea144_574x252.png) 注意 以上程序中,关于代码`#super().__init__()`的说明 这一行代码,可以调用也可以不调用,建议调用,因为`__init__`方法往往是用来对创建完的对象进行初始化工作,如果在子类中重写了父类的`__init__`方法,即意味着父类中的很多初始化工作没有做,这样就不保证程序的稳定了,所以在以后的开发中,如果重写了父类的`__init__`方法,最好是先调用父类的这个方法,然后再添加自己的功能 # 异常处理中抛出异常 ~~~ class Test(object): def __init__(self, switch): self.switch = switch #开关 def calc(self, a, b): try: return a/b except Exception as result: if self.switch: print("捕获开启,已经捕获到了异常,信息如下:") print(result) else: #重新抛出这个异常,此时就不会被这个异常处理给捕获到,从而触发默认的异常处理 raise a = Test(True) a.calc(11,0) print("----------------------华丽的分割线----------------") a.switch = False a.calc(11,0) ~~~ ![](https://box.kancloud.cn/558883693ea59b7194adffdbff7e7365_513x175.png) # 完整语法 ~~~ try: pass except 错误类型1: pass except 错误类型2: pass except (错误类型3,错误类型4): pass except Exception as result: # 打印错误消息 print(result) else: pass finally: print("无论是否有异常,都会执行代码") ~~~ # 记录错误 如果不捕获错误,自然可以让Python解释器来打印出错误堆栈,但程序也被结束了。既然我们能捕获错误,就可以把错误堆栈打印出来,然后分析错误原因,同时,让程序继续执行下去。 Python内置的logging模块可以非常容易地记录错误信息: ~~~ # err_logging.py import logging def foo(s): return 10 / int(s) def bar(s): return foo(s) * 2 def main(): try: bar('0') except Exception as e: logging.exception(e) main() print('END') ~~~ 同样是出错,但程序打印完错误信息后会继续执行,并正常退出: ~~~ $ python3 err_logging.py ERROR:root:division by zero Traceback (most recent call last): File "err_logging.py", line 13, in main bar('0') File "err_logging.py", line 9, in bar return foo(s) * 2 File "err_logging.py", line 6, in foo return 10 / int(s) ZeroDivisionError: division by zero END ~~~ 通过配置,logging还可以把错误记录到日志文件里,方便事后排查 # phd 启动Python的调试器pdb,让程序以单步方式运行,可以随时查看运行状态。我们先准备好程序: ~~~ # err.py s = '0' n = int(s) print(10 / n) ~~~ 然后启动: ~~~ $ python -m pdb err.py > /Users/michael/Github/learn-python3/samples/debug/err.py(2)<module>() -> s = '0' ~~~ 以参数-m pdb启动后,pdb定位到下一步要执行的代码-> s = '0'。输入命令l来查看代码: ~~~ (Pdb) l 1 # err.py 2 -> s = '0' 3 n = int(s) 4 print(10 / n) ~~~ 输入命令n可以单步执行代码: ~~~ (Pdb) n > /Users/michael/Github/learn-python3/samples/debug/err.py(3)<module>() -> n = int(s) (Pdb) n > /Users/michael/Github/learn-python3/samples/debug/err.py(4)<module>() -> print(10 / n) ~~~ 任何时候都可以输入命令p 变量名来查看变量: ~~~ (Pdb) p s '0' (Pdb) p n 0 ~~~ 输入命令q结束调试,退出程序: ~~~ (Pdb) q ~~~ 这种通过pdb在命令行调试的方法理论上是万能的,但实在是太麻烦了,如果有一千行代码,要运行到第999行得敲多少命令啊。还好,我们还有另一种调试方法。 **pdb.set_trace()** 这个方法也是用pdb,但是不需要单步执行,我们只需要import pdb,然后,在可能出错的地方放一个pdb.set_trace(),就可以设置一个断点: ~~~ # err.py import pdb s = '0' n = int(s) pdb.set_trace() # 运行到这里会自动暂停 print(10 / n) ~~~ 运行代码,程序会自动在pdb.set_trace()暂停并进入pdb调试环境,可以用命令p查看变量,或者用命令c继续运行: ~~~ $ python err.py > /Users/michael/Github/learn-python3/samples/debug/err.py(7)<module>() -> print(10 / n) (Pdb) p n 0 (Pdb) c Traceback (most recent call last): File "err.py", line 7, in <module> print(10 / n) ZeroDivisionError: division by zero ~~~ 这个方式比直接启动pdb单步调试效率要高很多,但也高不到哪去 # 断言 ~~~ def foo(s): n = int(s) assert n != 0, 'n is zero!' return 10 / n def main(): foo('0') ~~~ assert的意思是,表达式`n != 0`应该是True,否则,根据程序运行的逻辑,后面的代码肯定会出错。 如果断言失败,assert语句本身就会抛出AssertionError: ~~~ $ python err.py Traceback (most recent call last): ... AssertionError: n is zero! ~~~ 程序中如果到处充斥着assert,和print()相比也好不到哪去。不过,启动Python解释器时可以用`-O`参数来关闭assert: ~~~ $ python -O err.py Traceback (most recent call last): ... ZeroDivisionError: division by zero ~~~ 关闭后,你可以把所有的assert语句当成pass来看