Python 错误和异常

    xiaoxiao2022-07-13  147

    异常Exception

    错误Error:又称解析错误,是可以避免的 异常Exception:在没有出现上面错误的前提下,语句和语法都是正确的,本身是意外情况,异常是不可避免的

    In [1]: "asd" = 100 File "<ipython-input-2-2b4a41f6b880>", line 1 "asd" = 100 # 这是错误 ^ SyntaxError: can't assign to literal In [2]: with open("test") as f: ...: f.read() ...: Traceback (most recent call last): File "<ipython-input-1-22412c9ffa87>", line 1, in <module> with open("test") as f: # 这是异常 FileNotFoundError: [Errno 2] No such file or directory: 'test'

    在高级编程语言中,一般都有错误和异常的概念,异常可以捕获,错误不能被捕获 尽可能的避免错误 尽可能的捕获、处理异常

    产生异常

    产生

    raise语句显式的抛出异常Python解释器自己检测到异常并引发它 In [3]: print(1/0) # ZeroDivisionError In [4]: raise TimeoutError # raise抛出的异常

    异常的捕获

    try: 待捕获异常的代码块 except [异常类型]: 异常的处理代码块 In [5]: def fn(): ...: try: ...: print(1/0) ...: except ZeroDivisionError: # 指定捕获的异常类型 ...: print("ZeroDivisionError!") ...: ...: print("catch it!") ...: In [6]: fn() ZeroDivisionError! catch it!

    注意:如果写明异常类型,异常类型要与上面发生的异常匹配,否则捕捉不到

    In [7]: def fn(): ...: try: ...: print(1/0) print("hi") # 因为上面发生了异常,这一句并不会被执行 ...: except FileNotFoundError: # 发生的异常和指定的异常类型不匹配 ...: print("ZeroDivisionError!") ...: ...: print("catch it!") ...: In [8]: fn() Traceback (most recent call last): File "<ipython-input-8-e1d0f67027b9>", line 1, in <module> fn() File "<ipython-input-7-479fd82f5488>", line 3, in fn print(1/0) ZeroDivisionError: division by zero

    异常类及继承层次

    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

    各种异常类型关系

    BaseException及子类

    1.SystemExit

    sys.exit()引发的异常,异常不捕获处理,直接交给解释器,解释器退出 如果except该句捕获了该异常,则继续往后执行,如果没有捕获,解释器退出

    In [10]: try: ...: sys.exit(1) ...: except SystemExit: ...: print("SysExit") ...: print("catch it!") SysExit catch it!

    2.KeynoardInterrupt

    对应的中断行为Ctrl+C

    注:在解释器环境下使用这种捕获,普通ide环境热键冲突

    In [12]: import time In [13]: try: ...: while True: ...: time.sleep(1) ...: print("===") ...: except KeyboardInterrupt: ...: print("Ctrl + C") ...: print("==end==") === === === === Ctrl + C ==end==

    3.Exception及子类

    Exception是所有内建的、非系统退出的异常的基类

    注:python将有些语法错误归类到Exception下面,但这种错误是不可捕获的

    ArithmeticError:所有算数计算引发的异常,子类有:

    FloatPointErrorOverflowErrorZeroDivisionEroor

    LookupError : 使用映射的键或者序列的索引无效时引发的异常的基类

    IndexErrorKeyError

    自定义异常类 :

    class MyException(Exception): pass In [35]: class M(ArithmeticError): ...: pass ...: In [36]: try: ...: raise M() ...: except M: ...: print("catch it!") ...: catch it!

    这里有个坑,就是自定义的异常只能手动raise抛出,解释器不知道你定义的异常类是什么鬼,即使你是从别的异常类继承下来的类

    多种捕获

    except可以指定捕获的类型,捕获多种异常 多个except即可,但是之后最多匹配一个异常

    In [38]: try: ...: 1/0 ...: raise FileNotFoundError ...: open("test") ...: sys.exit(2) ...: except ZeroDivisionError: ...: print("catch 1") ...: except FileNotFoundError: ...: print("catch 2") ...: except Exception: ...: print("catch 3") ...: except: ...: print("catch 4") ...: print(5) catch 1 5

    捕获规则: ①从上到下依次比较,如果匹配,则执行匹配的except语句块 ②如果一个except语句捕获,其他except语句不会再次捕获了 ③如果没有任何一个except语句捕获这异常,则该异常向外抛出

    捕获的原则: 从小到大,从具体到宽泛

    as字句

    except ... as ...

    raise抛出的异常,应该都是异常类的实例 使用exception as e:查看e以及类型查看是否按要求捕获到

    In [40]: try: ...: raise 1 ...: except Exception as e: ...: print("catch it") ...: print(e) ...: catch it exceptions must derive from BaseException #

    rasie语句

    注意区分下面两种情况

    In [50]: class M(Exception): ...: def __init__(self, code, message): ...: self.code = code ...: self.message = message ...: # 1 In [51]: try: ...: raise M # 抛出的是类,但是这个类被解释器无参实例化,所以报参数错误 ...: except M as e: ...: print("catch it!") ...: except Exception as e: ...: print("111") ...: print(e) ...: print("===") 111 __init__() missing 2 required positional arguments: 'code' and 'message' === # 2 In [52]: try: ...: raise M(200,"ok") # 抛出的是类的实例化,所以被except M as e捕获 ...: except M as e: ...: print("catch it!") ...: except Exception as e: ...: print("111") ...: print(e) ...: print("===") catch it! === rasie后要求应该是BaseException类的子类或者实例化,如果是类,将被无参实例化raise后什么都没有,表示抛出最近一个被激活的异常,如果没有被激活的异常,则抛类型异常,这种方式很少用 In [53]: try: ...: raise FileNotFoundError ...: except: ...: raise # 抛出上面最近的一个被激活的异常FileNotFoundError ...: finally: ...: print("finally") ...: print("==end==") finally Traceback (most recent call last): File "<ipython-input-53-1200dd6fb7ab>", line 2, in <module> raise FileNotFoundError FileNotFoundError

    finally

    即最后一定要执行的语句块 可以在函数中使用finally,并且函数的返回值取决于最后一个执行的return语句 函数遇到真正的最后一个可以被执行的return就不会执行了

    In [57]: def f(): ...: # return 1 ...: try: ...: return 2 ...: finally: # 一定会被执行的语句块 ...: return 3 ...: return 4 ...: In [58]: f() Out[58]: 3

    注:如果except没有捕获到异常,finally结束后的后面的语句不会执行,最后会抛出一个异常

    异常的传递

    异常总是向外层抛出,一直抛出到被捕获,如果到了最外层还没被捕获,就会中断异常所在的线程的执行。

    def foo(s): return 10 / int(s) def bar(s): return foo(s) * 2 def main(): bar('0') main() Traceback (most recent call last): File "err.py", line 11, in <module> main() File "err.py", line 9, in main bar('0') File "err.py", line 6, in bar return foo(s) * 2 File "err.py", line 3, in foo return 10 / int(s) ZeroDivisionError: division by zero

    出错并不可怕,可怕的是不知道哪里出错了。解读错误信息是定位错误的关键。我们从上往下可以看到整个错误的调用函数链。 最后我们定位到错误的源头:foo函数中的10/0

    记录错误

    如果不捕获错误,自然可以让Python解释器来打印出错误堆栈,但程序也被结束了。既然我们能捕获错误,就可以把错误堆栈打印出来,然后分析错误原因,同时,让程序继续执行下去。

    Python内置的logging模块可以非常容易地记录错误信息:

    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')

    同样是出错,但程序打印完错误信息后会继续执行,并正常退出:

    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还可以把错误记录到日志文件里,方便事后排查

    这里转自廖雪峰博客 Python关于模块 logging — Python 的日志记录工具 后面详细介绍logging日志记录工具

    try嵌套

    内部捕获不到异常,回向外层传递异常 但是如果内部有finally且其中有return、break语句,则异常就不会继续往外抛出,异常就会被丢弃了

    In [2]: def foo(): ...: try: ...: 1/0 ...: except KeyError as e: # 异常不匹配,所以捕捉不到异常 ...: print(e) ...: finally: ...: print("foo!") ...: return 2 # 异常在这里被丢弃 ...: ...: try: ...: foo() ...: except: ...: print("catch") ...: else: # 因为上面的foo()函数并不会抛出异常,所以else语句正常执行 ...: print("good else") ...: finally: # finally一定执行 ...: print("end") ...: foo! good else end

    异常的捕获时机

    1.立即捕获,需要立即返回一个明确的结果

    比如遇到异常就 return 0

    In [3]: def parse_int(s): ...: try: ...: return int(s) ...: except: ...: return 0 # 遇到异常就返回0 ...: In [4]: parse_int([]) Out[4]: 0

    2.边界捕获

    封装产生了边界例如open函数,出现的异常交给调用处理,文件存在了,就不用了创建了,看是否修改还是删除根据实际环境选择捕获时机

    else字句

    else表示没有任何异常发生,则执行

    In [9]: try: ...: 0/1 ...: print("===") ...: except: ...: print("123") ...: else: # 上面没抛异常,这里会被执行 ...: print("success") ...: finally: ...: print("finally") ...: === success finally

    总结

    try: <语句> # 运行时的代码 except [异常类]: <语句> raise # 抛出上面收到的异常 except [异常类] as <变量名>: # 捕获某种异常并获得对象e <语句> else: # 如果没有任何异常发生则执行 <语句> finally: <语句> # 退出try时总会执行

    try的工作原理

    1.如果try语句执行时发生异常,搜索except字句,并执行第一个匹配该异常的except字句 2.如果try语句中语句执行时发生异常,却没有匹配的except字句,异常将被递交到外层的try,如果外层不处理这个异常,异常将继续将外层传递,如果到了最外层还没被处理,就终止异常所在的线程 3.如果try执行时没发生异常,如有else字句,那么这个else字句可以被执行 4.无论try是否发生异常,finally都会被执行

    python官方文档"错误和异常"

    最新回复(0)