本章概述:python基础——异常处理和调试
1、异常捕获 编程语言在程序运行的时候,难免会发生错误,特别是程序在调试的时候,如果程序在运行过程中发生错误,将会导致程序崩溃退出,但是很多时候我们并不希望程序退出,我们可能只是希望程序给我返回一个错误信息,然后程序能够继续运行,这时,我们就可以使用python的异常处理机制。Python中和异常相关的关键字有五个,分别是try
、except
、else
、finally
和raise
。
1 2 3 4 5 6 7 8 try : print (a) except NameError: print ("出现名字的异常了" ) else : print ('没有异常时执行的代码' ) finally : print ("结束" )
在python中,将需要检测的语句放在try代码块中,try后可以跟多个except代码块来捕获不同的异常并进行处理,else代码块是检测的代码不发生异常时执行的语句,不管是否发生异常finally都会执行。上面的代码由于a没有定义,产生了NameError异常,我们在except捕获这个异常并且执行相关语句。
2、内置异常类型 python内置了大量的异常类型,下面列一下部分异常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 BaseException +-- SystemExit +-- KeyboardInterrupt +-- GeneratorExit +-- Exception +-- StopIteration +-- StopAsyncIteration +-- ArithmeticError | +-- FloatingPointError | +-- OverflowError | +-- ZeroDivisionError +-- AssertionError +-- AttributeError +-- BufferError +-- EOFError +-- ImportError | +-- ModuleNotFoundError +-- LookupError | +-- IndexError | +-- KeyError +-- MemoryError +-- NameError | +-- UnboundLocalError +-- OSError | +-- BlockingIOError | +-- ChildProcessError | +-- ConnectionError | | +-- BrokenPipeError | | +-- ConnectionAbortedError | | +-- ConnectionRefusedError | | +-- ConnectionResetError | +-- FileExistsError | +-- FileNotFoundError | +-- InterruptedError | +-- IsADirectoryError | +-- NotADirectoryError | +-- PermissionError | +-- ProcessLookupError | +-- TimeoutError +-- ReferenceError +-- RuntimeError | +-- NotImplementedError | +-- RecursionError +-- SyntaxError | +-- IndentationError | +-- TabError +-- SystemError +-- TypeError +-- ValueError | +-- UnicodeError | +-- UnicodeDecodeError | +-- UnicodeEncodeError | +-- UnicodeTranslateError +-- Warning +-- DeprecationWarning +-- PendingDeprecationWarning +-- RuntimeWarning +-- SyntaxWarning +-- UserWarning +-- FutureWarning +-- ImportWarning +-- UnicodeWarning +-- BytesWarning +-- ResourceWarning
Python所有的异常都是BaseException的子类型,子类型中最常用的是Exception类型,他是常规异常类型的父类型,也就是说,我们程序运行碰到的大部分异常都是它的子类型。
3、自定义异常 当内置的异常类型不满足程序要求时,我们也可以自定义异常来满足我们的要求,自定义异常需要用到raise关键字,并且自定义异常也应该继承于Exception或其子类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class PwLen (Exception ): '''自定义异常类型''' pass def test (number ): if number<10 : raise PwLen('number不能小于10' ) return number try : t=test(9 ) except PwLen as p: print (p) else : print (t)
上面的例子是简单的自定义异常,我们还可以在自定义异常类中做更多的操作以满足不同的异常。
4、调试 程序编写过程中,经常需要进行调试,已保证能得到我们想要的结果,出现bug时,有的bug很简单,看看错误信息就知道,有的bug很复杂,我们需要知道出错时,哪些变量的值是正确的,哪些变量的值是错误的,因此我们需要有调试的手段来修复bug。
第一种调试的方法很简单也很粗暴,就是直接用print把可能出现的错误的值打印出来看看是不是正常,很多人都是直接用这种方法,包括我也是,在写脚本时经常用到print来打印变量,但是这种调试方法不好的地方就是一旦写print的地方太多的话,很混乱,并且不能一眼就看出bug所在,需要我们做判断。
第二种方法就是使用断言(assert),凡是用print来辅助查看的地方,都可以用断言(assert)来替代。
1 2 3 4 5 def N (n ): assert n!=0 ,'n不能为0' print (n) N(0 )
assert判断某个表达式的值时,如果结果为True,则程序继续执行,如果结果为False,则报AssertionError错误。在编写程序的时候,养成习惯,尽量使用断言去调试,而不是选择print。在运行程序时,可以加-O选项来忽略assert语句。
第三种方法是通过logging模块来进行调试,和assert相比,这种方法不会抛出错误,而是输入到文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 import loggingimport oscurrent_work_dir = os.path.dirname(__file__) LOG_FORMAT = "%(asctime)s - %(levelname)s - %(message)s" DATE_FORMAT = "%m/%d/%Y %H:%M:%S %p" logging.basicConfig(level=logging.DEBUG,filename=f'{current_work_dir} \log.txt' ,format =LOG_FORMAT,datefmt=DATE_FORMAT) logging.log(logging.DEBUG, "This is a debug log." ) logging.log(logging.INFO, "This is a info log." ) logging.log(logging.WARNING, "This is a warning log." ) logging.log(logging.ERROR, "This is a error log." ) logging.log(logging.CRITICAL, "This is a critical log." ) ''' 04/22/2022 10:33:25 AM - DEBUG - This is a debug log. 04/22/2022 10:33:25 AM - INFO - This is a info log. 04/22/2022 10:33:25 AM - WARNING - This is a warning log. 04/22/2022 10:33:25 AM - ERROR - This is a error log. 04/22/2022 10:33:25 AM - CRITICAL - This is a critical log. '''
logging有5中日志级别,默认是WARNING级别,因此logging.basicConfig不指定级别的情况下,DEBUG和INFO级别的信息是不输出的,如果想要输出所有级别的日志,需要将level设置为DEBUG。在编写复杂的程序时,日志信息是不可缺少的。