错误Error:又称解析错误,是可以避免的
异常Exception:在没有出现上面错误的前提下,语句和语法都是正确的,本身是意外情况,异常是不可避免的
In [1]: "asd" = 100
File "" , 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 "" , line 1, in <module>
with open("test") as f:
# 这是异常
FileNotFoundError: [Errno 2] No such file or directory: 'test'
在高级编程语言中,一般都有错误和异常的概念,异常可以捕获,错误不能被捕获
尽可能的避免错误
尽可能的捕获、处理异常
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 "" , line 1, in <module>
fn()
File "" , 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及子类
sys.exit()引发的异常,异常不捕获处理,直接交给解释器,解释器退出
如果except该句捕获了该异常,则继续往后执行,如果没有捕获,解释器退出
In [10]: try:
...: sys.exit(1)
...: except SystemExit:
...: print("SysExit")
...: print("catch it!")
SysExit
catch it!
对应的中断行为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==
Exception是所有内建的、非系统退出的异常的基类
注:python将有些语法错误归类到Exception下面,但这种错误是不可捕获的
ArithmeticError:所有算数计算引发的异常,子类有:
LookupError : 使用映射的键或者序列的索引无效时引发的异常的基类
自定义异常类 :
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语句捕获这异常,则该异常向外抛出
捕获的原则:
从小到大,从具体到宽泛
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 #
注意区分下面两种情况
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!
===
In [53]: try:
...: raise FileNotFoundError
...: except:
...: raise # 抛出上面最近的一个被激活的异常FileNotFoundError
...: finally:
...: print("finally")
...: print("==end==")
finally
Traceback (most recent call last):
File "" , line 2, in <module>
raise FileNotFoundError
FileNotFoundError
即最后一定要执行的语句块
可以在函数中使用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日志记录工具
内部捕获不到异常,回向外层传递异常
但是如果内部有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.边界捕获
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时总会执行
1.如果try语句执行时发生异常,搜索except字句,并执行第一个匹配该异常的except字句
2.如果try语句中语句执行时发生异常,却没有匹配的except字句,异常将被递交到外层的try,如果外层不处理这个异常,异常将继续将外层传递,如果到了最外层还没被处理,就终止异常所在的线程
3.如果try执行时没发生异常,如有else字句,那么这个else字句可以被执行
4.无论try是否发生异常,finally都会被执行
python官方文档"错误和异常"