Python使用被称为异常的特殊对象来表达执行期间发现的错误。当这些异常没有被捕获并处理时,程序将停止,并向控制台打印错误信息。这个错误信息通常是一个traceback,包含了异常的类型,以及诱发这个异常的代码位置及调用栈细节。

本文节选自作者的《Python编程基础及应用》视频教程。Python编程基础及应用_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili​www.bilibili.com

1. 曾经的异常

在本书的前半部分,我们已经遇到过很多异常:ValueError 值与期望的不符

IndentationError 代码缩进错误

IndexError 序列索引不存在

AssertionError 断言失败

NameError 名字不存在

KeyError 映射(比如字典)中的键不存在

AttributeError 属性错误(对象无指定名字的属性)

TypeError 类型出错

SyntaxError 代码语法错误

OSError 操作系统未能执行指定任务

ZeroDivisionError 除0错误

这些异常类,都继承自Exception类型。在本书的前半部分,我们对于这些异常采取了放任的态度: 程序直接报错停止。但一个严谨的程序,应该捕获并处理这些异常。异常发生后- 捕获并处理异常,尝试将程序从异常中拯救出来,继续正常运行。

- 捕获并处理异常,至少做一些必要的紧急操作,避免严重后果的发生。比如,汽车的车载控制系统发现发动机的“异常高温”(可能意味着起火)异常,应尝试切断油路,迫使汽车减速停车;一个股票交易系统发现无法恢复的异常,应尝试关闭数据库连接,并将没有保存的文件全部存盘。

- 捕获并处理异常,最低限度,作者认为应该将异常信息保存在错误日志中,以便程序员查找错误发生的原因。

2. try except else finally

下述代码展示了一个完整的异常处理程序:

#try.py

def divide(a, b):

return a/b

while True:

sFirst = input("First number:")

sSecond = input("Second number:")

if sFirst == "q" or sSecond == "q":

break

try:

iFirst = int(sFirst)

iSecond = int(sSecond)

fResult = divide(iFirst,iSecond)

except (ZeroDivisionError) as e:

print("You can not divide by 0:",e)

except (ValueError,TypeError) as e:

print("Illegal value been inputted:",e)

print(type(e))

except (Exception) as e:

print("An exception found, I do not know how to process it.")

raise

else:

print( sFirst, "/", sSecond, "=", fResult)

finally:

print("'Finally' will be executed what ever happens.")

上述程序试图让用户输入两个整数,然后相除并将相除的结果打印出来。当除数为0或者操作者输入的字符串不是一个整数时,均会发生异常。上述try ... except ... else ... finally语句将会捕获并恰当地处理这些异常。这个try...finally语句的工作过程可以概述如下:

首先,解释器将会执行try子句内的代码。在上例中,作者故意把try子句写得比较复杂,还加入了一个本不必要的函数,目的是想告诉读者:try子句内的代码以及间接被try子句内代码调用执行的代码,都受try子句的管辖。

如果try子句的执行没有发生异常,在try子句执行完毕后将执行else子句,然后再执行finally语句。下述输入及其执行结果证明了这一点。作者输入了15和3,字符串到整数的转换没有任何问题,else子句打印了除法运算的结果,finally子句接下来“强行”补充:"不管发生什么,我都来刷存在感"。

First number:15

Second number:3

15 / 3 = 5.0

'Finally' will be executed what ever happens.

如果try子句的执行发生了异常,则Python解释器会放弃try子句内后续代码的执行,并根据异常的类型创建一个异常对象。然后,解释器将从前往后逐一检查except子句括号里所包括的异常类型,当实际发生的异常属于该except子句括号内的异常类型时,该except子句将会被执行。最后,finally子句也会被执行。

下例中,我们输入了15和0x38。由于0x38不是一个十进制整数,所以iSecond = int(sSecond)这行代码诱发了一个 ValueError异常。解释器放弃了后续try子句的执行,并执行了第二个except子句以及finally子句。

First number:15

Second number:0x38

Illegal value been inputted: invalid literal for int() with base 10: '0x38'

'Finally' will be executed what ever happens.

下例中,我们输入了15和0。此时,divide函数内的a/b产生了ZeroDivisionError异常。解释器放弃了try子句后续代码的执行,并执行了第一个except子句以及finally子句。

当异常发生后,如果异常类型既不属于第一条except语句指定的类型,也不属于第二条except语句指定的类型,此时,第三条except语句多半可以捕获并处理异常。因为,绝大多数异常都是Exception类的子类型。注意,作者说的是绝大多数,也有一些异常,比如SystemExit,KeyboardInterrupt,不是Exception的子类。如果希望捕获并处理这些异常,可以直接使用下述型式的except子句:

except:

print("I found an exception that is not sub-class of Exception.")

如果捕获的异常在当前情境下处理不了,也可以接着向外抛:上述代码中第三条except子句中的raise即为该用途。如果直接raise,抛出的是原有异常。当然,也可以欺骗或者加工一下异常:raise ValueError("值错了!") 。

连续输入两个"q", 上述程序将会正常结束。

所谓的向外抛出异常,有必要解释一下。程序中的try...except...else...finally语句很可能处于另外一个try...finally语句的try子句中。所谓,抛出,就是本try...finally语句不处理该异常,扔给外面那个try...finally来处理。参见下述伪代码,我们看到,dummy函数内的try...except间接处于外部的try...except的try子句管辖内。

def dummy():

try:

doing something here

except:

raise

try:

doing something before

dummy()

doing something more

except:

print("Exception catched...")try...finally语句总结- 当try子句的执行没有发生异常时,else子句将被执行。

- 当try子句的执行发生异常时,解释器会放弃执行try子句后续代码,并根据异常的类型选择执行except子句,顺序为从上到下。 |

- except子句捕获异常后,可以尝试将程序从异常中恢复,或者做一些最低限度的后处理,以避免“灾难”性结果。如果处理不了,也可以通过raise语句将异常外抛。

- 不管有没有异常发生,finally子句总会在最后阶段被执行。这使得finally子句特别适合于处理一些善后工作,比如关闭因为异常未及关闭的文件,断开网络连接,关闭数据库连接等。

- 语法上,else子句以及finally子句是可选的,而except子句可以有无限多条。

- Python允许程序员定义自己的异常类型,比如车载电脑的控制程序可能需要一个“发动机转速过高”的异常。这很简单:定义一个Exception的子类就好了。

3. 警告

如果有些情况的发生还不是那么严重,可以尝试发出警告。

#warn.py

from warnings import warn

def divide(a,b):

fResult = a / b

if fResult < 0.0000001:

warn("The result is very close to Zero!")

return fResult

print(divide(0.1, 10000000000))

print("Something else.")

执行结果:

d:\pylearn\C12_UnitTestException\warn.py:6: UserWarning: The result is very close to Zero!

warn("The result is very close to Zero!")

1.0000000000000001e-11

Something else.

从执行结果可以看出,warn()函数发出了警告,该警告会被打印至屏幕,但程序的执行不会因为该警告而停止:print("Something else.")在警告之后继续执行了。

如果你使用别人的模块,还可以通过filterwarnings()函数来过滤该模块产生的警告信息。你可以选择忽略-"ignore",也可以上纲上线地把该模块的警告转化成异常-"error"。具体请查询Python文档。

4. 出错日志

异常发生后,如果异常是预料中的类型,程序员直接处理这些异常并挽救程序即可。而程序发生非意料的异常几乎是历史的必然:程序员是人不是神! 没有bug的应用程序不存在。这些意料之外的异常发生后,把相关情况记入出错日志(通常是一个文本文件),对于解决问题,十分重要。下述代码中的UserExceptHook函数来自于作者编写的一个实际的应用软件。

#excepthook.py

import sys,traceback

from datetime import datetime

fError = open("except_error.log", 'a')

def UserExceptHook(tp, val, tb):

traceList = traceback.format_tb(tb)

html = repr(tp) + "\n"

html += (repr(val) + "\n")

for line in traceList:

html += (line + "\n")

print(html, file=sys.stderr)

print(datetime.now(), file=fError)

print(html, file=fError)

fError.close()

def main():

sFirst = input("First number:")

sSecond = input("Second number:")

try:

fResult = int(sFirst) / int(sSecond)

except Exception:

print("发现异常,但我不处理,抛出去.")

raise

else:

print( sFirst, "/", sSecond, "=", fResult)

sys.excepthook = UserExceptHook

main()

fError.close()

sys模块下的excepthook是一个勾子-hook函数。当有程序没有捕获的异常,或者捕获后又抛出来的异常时,解释器就会执行这个勾子函数,然后停止运行。

上述代码自行定义了一个勾子函数,并将其赋值给sys.excepthook。这个函数有三个参数:tp-异常类型、val-异常值、tb-异常跟踪栈。异常跟踪栈可以通过traceback模块的format_tb()函数转换成一个字符串列表,这些字符串表明了异常发生时的程序调用关系。

可以看到,UserExceptHook()函数把异常转换成一个多行字符串,其中,repr()函数将一个对象转换成一个可以打印的表示字符串。然后这个多行字符串被打印至sys.stderr标准错误输出,然后再被打印至fError文件。请注意,fError文件在程序开始执行时即被以"a"-附加模式打开,附加模式保证了后续发生的错误信息不会覆盖原有文件内容。此外,在UserExceptHook()函数的最后一行,fError文件被关闭。因为,该勾子函数执行完成后,程序将会终止,这里是唯一关闭文件确保文件内容被正确写入外存的最后机会。

datetime.now()返回当前系统日期和时间。

作者多次运行上述程序,故意输入一些不恰当的值来诱发异常,最后得到下述错误日志-except_error.log:

2018-11-03 18:05:58.305337

ZeroDivisionError('division by zero')

File "D:/pylearn/C12_UnitTestException/excepthook.py", line 28, in

main()

File "D:/pylearn/C12_UnitTestException/excepthook.py", line 20, in main

fResult = int(sFirst) / int(sSecond)

2018-11-03 18:06:04.097457

ValueError("invalid literal for int() with base 10: '3.2'")

File "D:/pylearn/C12_UnitTestException/excepthook.py", line 28, in

main()

File "D:/pylearn/C12_UnitTestException/excepthook.py", line 20, in main

fResult = int(sFirst) / int(sSecond)

终于,当使用你编写的软件的客户通过电话向你抱怨程序有时会运行出错时,你可以要求客户把错误日志文件发送给你。通过错误日志,你可以了解软件出错的相关细节,而不必通过客户那些“不准确”甚至“夸大其词”的描述去推断错误发生的原因。

本文节选自作者的B站MOOC及同名教材:Python编程基础及应用 — 重庆大学 高等教育出版社,作者亲授_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili​www.bilibili.com版权声明 本文可以在互联网上自由转载,但必须:注明出处(作者:海洋饼干叔叔)并包含指向本页面的链接。 本文不可以以纸质出版为目的进行改编、摘抄。

python报错输出到日志_Python下的异常处理及错误日志记录相关推荐

  1. python报错输出到日志_Python 日志配置总结

    日志是程序调试的利器,通过日志来解析获取数据,线上问题通过日志分析和定位等,用途多种多样.Python 作为当前常用的一门开发语言,国内很多开发者对其的使用极其粗糙,写法较为随意,受开发者本身熟悉的语 ...

  2. python报错输出到日志_Python日志记录和子进程输出和错误流

    基于Adam Rosenfield's code,你可以 >使用select.select来阻止,直到有要读取的输出 proc.stdout或proc.stderr, >然后读取并记录该输 ...

  3. Python报错 TypeError: Descriptors cannot not be created directly

    Python报错TypeError: Descriptors cannot not be created directly 具体错误: TypeError: Descriptors cannot no ...

  4. Python报错TypeError: Descriptors cannot not be created directly

    Python报错TypeError: Descriptors cannot not be created directly 具体错误: TypeError: Descriptors cannot no ...

  5. 已安装Anaconda情况下,命令行pip,python报错(详细 已解决)

    已安装Anaconda情况下,命令行pip,python报错(已解决) 这是报错截图 解决方案如下: 1.首先可以去找到anaconda文件夹,并打开该文件目录下的Script文件夹,查看是否有pip ...

  6. python报错怎么看_python中的错误如何查看

    python常见的错误有 1.NameError变量名错误 2.IndentationError代码缩进错误 3.AttributeError对象属性错误 4.TypeError类型错误 5.IOEr ...

  7. python 报错 IndentationError: expected an indented block SyntaxError: invalid character in identifie

    红色方框那里敲击一个空格就好! 输入要在全英情况下! 另外,还要注意括号的事情.括号别出错误! IndentationError: expected an indented block的报错: Syn ...

  8. 解决Python报错UnicodeDecodeError: 'gbk' codec can't decode byte 0x80 in position 658: illegal multibyte

    解决Python报错–UnicodeDecodeError: 'gbk' codec can't decode byte 0x80 in position 658: illegal multibyte ...

  9. 电脑安装python为什么显示的是程序丢失-python报错:无法启动此程序,因为计算机中丢失...

    原标题:python报错:无法启动此程序,因为计算机中丢失 python报错:无法启动此程序,因为计算机中丢失api-ms-win-crt-runtime-|1-1-0.dll api-ms-win- ...

最新文章

  1. 流程控制关键字——分支结构
  2. python定义函数的命令_Python入门 | 定义函数
  3. python中的对象列表_Python内建的对象列表
  4. Jenkins 无法运行 putty.exe问题解决
  5. linux man命令汉化 操作
  6. 8.RabbitMQ实战 --- 从Web端管理RabbitMQ
  7. 用JavaScript实现按钮点击全选和下拉列表根据省份复选框显示对应城市功能
  8. 怎样裁剪证件照片尺寸?怎么将电子证件照裁剪成一寸?
  9. 5G布控球星光级400万高清布控球智能布控球
  10. 退出CrOS Factory,进入正常Chrome OS
  11. 裸辞两个月,海投一个月,从 Android 转战 Web 前端的求职之路
  12. Mac安装brew 及安装报错的解决办法【已解决】
  13. Java的输入/输出
  14. Unity五子棋游戏设计 和简单AI实现(1)
  15. 风雨砥砺,岁月如歌——Ts之箭头函数
  16. Minecraft 1.16.5 生化8 模组 1.8版本 版本同步+支持服务器联机
  17. java 记事本源代码_JAVA记事本源代码 收藏
  18. 机器人将颠覆零售业,看AI在零售行业有哪些应用?
  19. 下载vue模板框架并使用
  20. 读《创造高收益的阿米巴模式》理解阿米巴的核心

热门文章

  1. 超火爆思维导图工具TheBrain 11到底有哪些值得关注的功能?
  2. python-socket编程-一个简单的嗅探器
  3. 健康上链——医疗健康行业数字化转型的关键路径|链塔智库
  4. 考研计算机南大和武大难度,全国高校考研录取难度排行榜,浙大第五、南大第十!武大掉出前十...
  5. swoft自定义进程
  6. 12.04怎样查看更改屏幕显示的刷新频率
  7. LIFT: Learned Invariant Feature Transform详细笔记
  8. mqtt 串口_全网通4G工业路由器模块和串口转网口/4G/有线/WiFi/LTE模块的实现原理 - 博晶网络的工程师...
  9. 一周新闻纵览:日本电子支付遭盗刷,浏览器历史或暴露隐私,程序员入侵67万台计算机,7000余款App被处理
  10. oracle支持xp吗,xp系统能装oracle10g吗解决方案