
异常处理模块能帮助我们在运行期间处理异常信息,但Python代码还有更为基础的错误——语法错误和逻辑错误。
语法错误相对简单,在解释器的帮助下,我们很快就能定位错误所在。但对逻辑错误的调试就难多了,这些语法或逻辑层面的错误,构成了各式各样的代码bug。
为了调试错误,我们需要知道,出错时哪些变量的值是正确的,哪些变量的值是错误的。因此,我们需要掌握一些代码调试的基本技巧。
本文选自《Python极简讲义:一本书入门数据分析与机器学习》一书。这是一本图文并茂、简单易读的Python极简讲义,以掌握“最少必要知识”为写作理念,对初学者十分友好!
第一种方法,简单而有效,直接而粗暴,就是用print()把需要观察的变量打印出来,如下所示。
【例1】 print()输出观察变量(print-err.py)
01 def foo(s):
02
n = int(s) #字符串转换为整型
03 print('n = {}'.format(n)) #输出观察变量n的值
04 return 10 / n
05
06 foo('0')
运行结果
n = 0
---------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
……
input-9-d21f20e5d17e> in foo(s)
2 n = int(s)
3 print('n = {}'.format(n))
----> 4 return 10 / n
5
6 def main():
ZeroDivisionError: division by zero
根据打印处的信息(第3行)和错误信息(division by zero),我们可以很容易地定位错误所在:代码第4行,作为分母,n值为0。
用print()观察变量的不足之处在于,调试完毕后,我们还得手动将它们删掉,如果调试工作量较大,造成print()满天飞,删除大量print()语句的工作量也不容小觑。而且,如果程序中到处充斥着print()语句,输出信息也会非常繁杂,给程序员造成困扰。
因此,就有了第二种方法—断言(assert)。凡是可用print()来辅助查看的,都可以用assert来替代。它用来测试某个条件(condition)的布尔值,系统默认这个条件为真,此时断言悄然无息,我们感知不到它的存在。但是,一旦条件为假,就会触发异常。assert的语法格式如下。
assert #第一种情况,不给出错误信息
在Python中,可以把assert理解为简化版的异常处理,它与如下语句等价。
if not
raise AssertionError
assert后面也可以紧跟参数,给出更为详细的错误信息,示例如下。
assert [, arguments] #第二种情况,给出错误信息(可选项)
这种情况等价于如下语句。
if not condition:
raise AssertionError(arguments)
下面我们通过具体示例来说明assert的用法,见【例2】。
【例2】assert的用法(assert_no_err_msg.py)
01 def avg(score):
02 assert len(score) != 0
03 return sum(score) / len(score)
04
05 score = []
06 print("平均分数为:",avg(score))
运行结果
AssertionError Traceback (most recent call last)
input-11-56d552b0cddd> in <module
>
4
5 score = []
----> 6 print("平均分数为:",avg(score))
input-11-56d552b0cddd> in avg(score)
1 def avg(score):
----> 2 assert len(score) != 0
3 return sum(score)/len(score)
4
5 score = []
AssertionError:
代码分析
由于代码的第05行是一个空列表,其长度为0,因此会让第02行的判断条件len(score) != 0 为假,这时就会触发异常,导致程序终止运行。此时,如果将第05行代码修改如下:
05 score = [90,85,78]
整个程序将能正常运行,运行结果如下。
平均分数为: 84.33333333333333
使用assert的好处在于,当判断条件为真时,用户是感觉不到assert的,因为assert只有当判断条件为假时才“刷存在感”,给出错误信息。错误信息一旦给出,在某种程度上就定位了代码的bug所在,从而达到了程序调试的目的。调试完毕后,用户无须删除assert语句。
【例2】中的assert并没有给出错误信息,可读性不强。事实上,我们还可以显式给出错误信息。我们可以如下修改【例2】的第02行代码。
assert len(marks) != 0, "列表为空,咋整啊!"
这里,断言条件后面的"列表为空,咋整啊!",就是条件一旦为假时输出的错误信息。我们假设,此时第05行依然为空列表,这时【例2】的运行结果如下。
AssertionError Traceback (most recent call last)
input-13-a477886d663d> in <module>
5 score = []
6 # score = [90,85,78]
----> 7 print("平均分数为:",avg(score))
input-13-a477886d663d> in avg(score)
1 def avg(score):
----> 2 assert len(score) != 0, "列表为空,咋整啊!"
3 return sum(score) / len(score)
4
5 score = []
AssertionError: 列表为空,咋整啊!
很明显,有了错误信息,就更容易找到代码的错误所在了。
如果断言太多,也会遭遇与print()类似的处境,异常信息会让我们“应接不暇”。如果不需要断言来帮忙,则在命令行启动Python解释器时可用“-O”参数来关闭assert,如下。
python -O assert_no_err_msg.py #选项是大写的字母O,而非数字0
除了前面提到的利用print()、assert进行调试,我们还可以使用IDE(如PyCharm等)进行调试,这些集成开发环境有着非常好用的“单步调试功能”,同时配合控制台的输出,也能比较便捷地定位错误。
当我们开发的项目规模比较大时,我们会发现,logging才是终极武器。logging是Python的日志模块。使用这个模块的好处在于,它允许我们指定记录信息的级别,有debug、info、warning、error等。
我们可以根据需要输出不同级别的信息。例如,当我们指定level=INFO时,logging.debug就不起作用了。同理,指定level=WARNING后,debug和info就不起作用了。这样一来,我们就不必担心太多输出信息会冲淡关注力。
关于这个模块的知识,就留给“爱折腾”读者自学吧。高手,永远都是自学出来的!
《Python极简讲义:一本书入门数据分析与机器学习》是一本理论结合实战,娓娓道来的入门好书。书中提供了入门数据科学领域的 极简必要知识 !
不同于其他图书,本书不求全,但求精。这里的“精”是指,给出入门数据分析的“极简必要知识"( Minimal Actionable Knowledge and Experience,MAKE )。在极短的时间内,掌握数据分析的MAKE之道,这是本书的一大特色。
▊ 本书脉络

▊ 大咖推荐
小编为我们喜爱阅读的书迷们免费送书。
留言点赞:留言分享您在学习或者使用python开发过程中的学习经验,或者其他一些学习经历。
玩法1:机会总是靠自己去争取来的!小编将对留言进行精选,被精选的评论将会显示在页面中(由于微信功能限制,最多只能显示100条)。活动截止时获得点赞数最多的前2名朋友获得《Python极简讲义:一本书入门数据分析与机器学习》纸质书1本。玩法2:踩楼送书活动,活动结束时踩中已放在百度云文件中指定楼层的精选留言将获得《Python极简讲义:一本书入门数据分析与机器学习》一本,共3名中奖者,活动结束
后我们会在本公众号公布中奖楼层的解压密码
获奖楼层下载地址(文件解压密码会在2020年5月12日推送的中奖文章中公布)
百度云链接:
https://pan.baidu.com/s/1i_WGN3QG02iKRWFJ7-RwKw
提取码: kx5h
特别申明:
1、一位用户1个月内只能有1次获奖机会,让更多粉丝受益活动才更有意义
2、每一位用户只有1次留言机会,不允许重复留言~
请大家记住这三个1哦
活动截止时间:2020 年 05 月 12日 16:00 整。

