怎么调试存储过程_第九章(第2节):调试

在码农群体中,有句话叫写代码就是写 bug,项目程序能一次写完并正常运行的概率几乎为 0,当然简单的 demo 除外。程序代码总会有各种各样的 bug 需要修正,有些 bug 很简单,看看错误信息就知道,有些 bug 很复杂,对于复杂的 bug,我们需要一整套调试程序的手段来找到它。

对于这种排查 bug 的方法有三种主流方式:一种方式是我们用 print 把可能出问题的变量打印到屏幕上,方便我们肉眼看到; 还有一种方式是通过调试工具单步运行,把程序执行动作放慢,我们可以观察每一步程序执行的过程, 这样我们不但能看到变量在内存中的值,还可以看到函数调用堆栈; 第三种方式是输出日志到磁盘文件进行记录,这样对于服务器程序用的比较多, 一个服务程序需要长期运行着,有可能前半年都没有出问题,后面某一天突然出现了问题, 我们不可能在程序运行的一开始就实时的去 print 或者去调试半年,我可以把可能会出现问题的地方做日志记录(输出到磁盘)方便我们在出问题后去查找。

本节课我们就学习以上三种方式,重点我们学习调试方式。

print 打印方式

假定有一道题:写一个函数,查找给定 mylist 中的最大值,并返回一个 dict,返回值的样式为:{"max": max}。 下面是给出我们一开始写出的实现代码。

''''
算法思想:
1.先假定 mylist 中第一个元素为最大值,并把该值存储在存放最大值的变量中。
2.遍历 mylist,取出的元素和存储最大值的变量作比较,如果该元素大于存储最大值的变量,则把该元素赋值给存储最大值的变量。
3.一直遍历到最后一个元素,并返回 maxdict。
'''

def findmax(mylist):
    max = mylist[0]  # 先假定第一个元素是最大值
    maxdict = {"max": max}

    for item in mylist:
        if item > max:
            max = item

    return maxdict


mylist = [4, 8, 9, 3]
print(findmax(mylist))

最后我们发现打印出的值为:{"max": 4},这是怎么回事呢,我们程序的语法虽然没有什么问题, 就有可能是我们程序逻辑的问题了,这个时候我们可能会想,是不是我们的算法有问题,我们就会想到,在循环中每次打印出 max 的具体值,来查找程序所出现的问题。

def findmax(mylist):
    max = mylist[0]  # 先假定第一个元素是最大值
    maxdict = {"max": max}

    for item in mylist:
        if item > max:
            max = item
            print(max)      # 8,9
            print(maxdict)  # {'max': 4},{'max': 4}

    return maxdict


mylist = [4, 8, 9, 3]
print(findmax(mylist))

通过打印,我们发现我们的算法没有问题,max 变量每次在循环内判断语句后的值都符合逻辑,但是 maxdict 的值并没有变化,由此我们知道,maxdict 的 "max" 键对应的值 max 和我们变量 max 不在同一块内存。 通过我们的思考和回忆,我们终于迷雾顿开,这个问题可以变化为:a = 4,b = a,a = 8, b 还是 4,a的变化并没有改变 b 的值一个道理。我们把代码修改成如下,正确解决 bug。

def findmax(mylist):
    max = mylist[0]  # 先假定第一个元素是最大值
    maxdict = {"max": max}

    for item in mylist:
        if item > maxdict["max"]:
            maxdict["max"] = item

    return maxdict


mylist = [4, 8, 9, 3]
print(findmax(mylist))

调试

通过 print 方式来查找程序的 bug,并不是很直观,特别是在程序比较复杂的时候,函数调用层次比较深, 循环次数比较多等等。如果有一种方法可以看到程序每一步执行的顺序,变量在内存中的值,以及函数调用的堆栈,这样程序执行的逻辑就可以在视觉上清晰的反馈给码农, 这种查找程序问题的方式叫调试,Pycharm IDE 集成了调试器,我们可以很方便的对程序进行调试。 调试的第一步就是下断点,我们用鼠标在代码的左侧点击左键就可以下断点,如果下图。

0bae3042b592b6a988b338f0159aad59.png

下好断点后,我们就可以对程序进行调试了,调试方法:在程序文件中右键,然后点击 debug文件名按钮。

bb67d2fca0e9b336a9f535b0ce1f60c6.png

进入调试状态后,我们现在开始点击 Run to Cursor按钮,解释器就会继续执行到它应该执行的地方停下来, 我们可以让程序在任意断点处停下来,来观察当前每个变量中的内存值,这样很方便我们排查错误。 下面我们看下调用的函数在循环执行完之后,断点执行到返回值的那一步时的各个变量的内存值,通过调试我们看到 maxdict 中的 max 值和 max 变量值不是同一个值,我们就会想到两个变量不是同一块内存。

1f0e8e9fac0c0c9cc4af96e92fcd028f.png

到目前为止我们给大家演示了如何使用 Pycharm 自带的调试器对程序下断点,断点调试等功能,当然调试器的功能 远远不止这些,比如我们可以查看函数调用堆栈等等,大家可以在项目开发过程中,熟练使用调试。

打日志

大家想想,如果我们的程序在一开始运行时没有出现问题,可能在经过一点时间运行后才出现了莫名其妙的问题, 这个时候我们靠 print 输出和下断点调试是没办法找出问题的。比如我们写的一个服务器程序,跑了半年都没有出现问题, 在半年后的某一天出现了问题,我们不可能用 print 打印半年吧,也不可能人为的下断点调试半年。 此时我们可以使用打日志的方式。

import logging
import time

logging.basicConfig(filename='mylog.log',
format='[%(asctime)s-%(filename)s-%(levelname)s:%(message)s]',
level=logging.DEBUG, filemode='a', datefmt='%Y-%m-%d%I:%M:%S %p')

def reciprocal(data):
    for item in data:
        time.sleep(1)   
        try:
            print("%f" % (1 / item))
        except:
            logging.error("出现错误,此时除数为:" + str(item))

mylist = [2.0, 9.0, 0.0, 1.0, 5.0]  # 假定这写是用户给的做除数的数据
reciprocal(mylist)                  # 我们求倒数

本节重要知识点

会使用 Pycharm 自带的调试功能。

养成使用调试技能找程序 bug 的习惯。

作业

写一个类,里面有属性和函数,类外面定义对象,然后调用函数,调试一遍,看看运行运行的过程。

阅读原文:

调试​www.birdpython.com
13d0d191824737f564d9a75a54bc9081.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值