零基础学 Python——断点调试

亲爱的亦菲彦祖们,欢迎继续跟随我们的编程学习系列!在前两篇博客中,我们已经掌握了模块和包以及异常处理的核心概念与应用。今天,我们将探讨Python编程中另一个至关重要的技能——断点调试。掌握断点调试不仅能帮助您快速定位和解决代码中的问题,还能提升您的编程效率和代码质量。让我们一起来深入了解断点调试的原理和实践吧!

一、了解调试

**调试(Debugging)**是编程过程中用于查找和修正代码错误的过程。通过调试,开发者可以逐步执行代码,观察变量的变化,理解程序的执行流程,从而发现并解决潜在的问题。

调试的重要性

  • 快速定位问题:有效的调试方法可以帮助您迅速找到代码中的错误位置。
  • 理解代码执行:通过调试,您可以更深入地理解代码的运行机制和逻辑。
  • 提升代码质量:及时发现和修复错误,确保代码的稳定性和可靠性。

二、启动调试模式

1. Debug简介

在Python开发中,调试工具(如PyCharm的Debugger)提供了丰富的功能,帮助开发者在代码执行过程中进行实时监控和控制。常见的调试功能包括:

  • 设置断点:暂停代码执行的指定位置。
  • 单步执行:逐行执行代码,观察每一步的执行结果。
  • 变量监视:查看和监控变量的值。
  • 表达式评估:在调试过程中计算和检查表达式的值。

2. 设置断点

**断点(Breakpoint)**是调试过程中用来暂停程序执行的标记。通过在代码中设置断点,您可以在程序执行到该位置时暂停,便于检查当前状态和变量值。

设置断点的方法

  1. 在PyCharm中

    • 打开要调试的Python文件。
    • 在代码行号旁的灰色边栏(gutter)单击,断点将以红色圆点显示。

  2. 注意事项

    • 仅在关键位置设置断点,以避免调试过程过于冗长。
    • 断点应设置在可能出问题或需要检查的代码行。

3. 启动调试

启动调试会话

  1. 在PyCharm中

    • 点击导航栏上的“Run”菜单。
    • 选择“Debug”选项,或者使用快捷键(通常是Shift + F9)。

  2. 调试会话界面

    • 调试过程中,PyCharm会显示一个“调试工具”窗口,其中包含变量、监视器和控制台等面板。

  3. 运行程序并触发断点

    • 运行调试会话后,程序会执行到第一个断点处暂停。
    • 此时,您可以查看当前的变量值和程序状态。

三、详细调试技巧

1. 单步调试

单步调试允许您逐行执行代码,观察每一步的执行情况。这对于理解代码的执行流程和发现逻辑错误尤为重要。

单步调试的方法

  • Step Over (F8):执行当前行,并跳到下一行。如果当前行包含函数调用,Step Over不会进入函数内部,而是等待函数执行完毕。

  • Step Into (F7):进入函数内部,逐步执行函数中的代码。

  • Step Out (Shift+F8):跳出当前函数,返回到调用该函数的位置。

  • Resume Program (F9):继续执行程序,直到下一个断点或程序结束。

示例操作

假设您在以下代码中设置了断点:

if __name__ == '__main__':
    my_car = Car()
    print("我是一辆车!")
    my_car.main()

调试过程中,您可以使用Step Over逐行执行,观察变量my_car的状态和方法main()的执行过程。

2. 监视变量

**监视(Watches)**功能允许您实时查看和监控变量的值,甚至可以添加自定义表达式进行评估。

设置监视

  1. 在调试工具窗口中,找到“Watches”面板。

  2. 点击“+”按钮,输入要监视的变量名或表达式,例如my_car.speed

示例

print(my_car.speed)

在断点处,您可以监视my_car.speed的值,了解汽车当前的速度状态。

3. 内联调试

**内联调试(Inline Debugging)**是在编辑器中直接显示变量的值,无需切换到“Variables”面板,便于快速查看变量状态。

启用内联调试

  1. 在PyCharm中,进入“Settings”或“Preferences”。
  2. 搜索“Inline Debugging”并启用相关选项。

示例

在调试过程中,变量的值会直接显示在代码行旁边,便于快速识别问题。

4. 计算表达式

计算表达式功能允许您在调试过程中手动输入并计算任意表达式,以验证其结果或检查变量间的关系。

使用方法

  1. 在调试工具窗口中,点击“Evaluate Expression”按钮。

  2. 在弹出的对话框中,输入要计算的表达式,例如my_car.odometer / my_car.time

  3. 点击“Evaluate”按钮查看结果。

示例

def average_speed(self):
    return self.odometer / self.time

在调试过程中,您可以手动计算my_car.odometer / my_car.time,确认平均速度的正确性。

5. 常用快捷键

掌握调试的快捷键,可以大幅提升调试效率。以下是PyCharm中常用的调试快捷键:

  • Step Over:F8
  • Step Into:F7
  • Step Out:Shift + F8
  • Resume Program:F9
  • Evaluate Expression:Alt + F8
  • Toggle Breakpoint:Ctrl + F8
  • Run to Cursor:Alt + F9

快捷键示例

  • 设置断点:点击代码行旁边的gutter,或使用Ctrl + F8
  • 单步执行:使用F8逐行执行,F7进入函数内部。
  • 继续执行:按F9跳至下一个断点。

四、实战案例:调试学生信息管理系统

为了更好地理解断点调试的应用,我们将以之前设计的学生信息管理系统为例,进行详细的调试操作。

项目结构

student_management/
├── __init__.py
├── student.py
└── manager.py

示例代码

# student.py

class Student:
    """学生信息类"""
    def __init__(self, name, chinese, math, english):
        """
        初始化方法
        :param name: 姓名
        :param chinese: 语文成绩
        :param math: 数学成绩
        :param english: 英语成绩
        """
        self.name = name
        self.chinese = chinese
        self.math = math
        self.english = english
        self.total = chinese + math + english  # 总分

    def __str__(self):
        return f"{self.name}\t{self.chinese}\t{self.math}\t{self.english}\t{self.total}"
# manager.py

import json
from .student import Student

class StudentManager:
    """学生信息管理系统类"""
    def __init__(self):
        # 存储学员数据 -- 列表
        self.student_list = []

    # 程序入口函数
    def run(self):
        # 加载文件里面的学员数据
        self.load_student()

        while True:
            # 显示功能菜单
            self.show_menu()
            # 用户输入目标功能序号
            action = input('请选择您想要进行的操作(0-退出):')

            # 根据用户输入的序号执行不同的功能
            if action == '1':
                # 新建学生信息
                self.add_student()
            elif action == '2':
                # 显示全部信息
                self.show_student()
            elif action == '3':
                # 查询学生信息
                self.search_student()
            elif action == '4':
                # 删除学生信息
                self.del_student()
            elif action == '5':
                # 修改学生信息
                self.modify_student()
            elif action == '0':
                # 退出系统
                self.save_student()
                print('欢迎再次使用【学生信息管理系统】!再见!')
                break
            else:
                print('输入错误,请重新选择!')

    # 显示功能菜单
    @staticmethod
    def show_menu():
        str_info = """**************************************************
欢迎使用【学生信息管理系统】V1.0
请选择你想要进行的操作
1. 新建学生信息
2. 显示全部信息
3. 查询学生信息
4. 删除学生信息
5. 修改学生信息

0. 退出系统
**************************************************"""
        print(str_info)

    # 添加学生信息
    def add_student(self):
        print('---------- 新建学生信息 ----------')
        name = input('请输入姓名:').strip()
        if not name:
            print('姓名不能为空!')
            return
        try:
            chinese = int(input('请输入语文成绩:'))
            math = int(input('请输入数学成绩:'))
            english = int(input('请输入英语成绩:'))
        except ValueError:
            print('成绩必须是整数!')
            return

        # 检查是否已有该学生
        if any(stu.name == name for stu in self.student_list):
            print(f'学生{name}已存在,无法重复添加!')
            return

        # 创建学生对象并添加到列表
        student = Student(name, chinese, math, english)
        self.student_list.append(student)
        print(f'成功添加学生:{student.name}')

    # 显示全部学生信息
    def show_student(self):
        print('---------- 显示全部学生信息 ----------')
        if not self.student_list:
            print('当前没有学生信息。')
            return
        print('姓名\t语文\t数学\t英语\t总分')
        print('-' * 30)
        for stu in self.student_list:
            print(stu)

    # 查询学生信息
    def search_student(self):
        print('---------- 查询学生信息 ----------')
        search_name = input('请输入要查询的学生姓名:').strip()
        if not search_name:
            print('姓名不能为空!')
            return
        for stu in self.student_list:
            if stu.name == search_name:
                print('姓名\t语文\t数学\t英语\t总分')
                print('-' * 30)
                print(stu)
                return
        print(f'未找到名为"{search_name}"的学生。')

    # 删除学生信息
    def del_student(self):
        print('---------- 删除学生信息 ----------')
        del_name = input('请输入要删除的学生姓名:').strip()
        if not del_name:
            print('姓名不能为空!')
            return
        for stu in self.student_list:
            if stu.name == del_name:
                confirm = input(f'确定要删除学生"{del_name}"吗?(y/n):').strip().lower()
                if confirm == 'y':
                    self.student_list.remove(stu)
                    print(f'成功删除学生:{del_name}')
                else:
                    print('删除操作已取消。')
                return
        print(f'未找到名为"{del_name}"的学生。')

    # 修改学生信息
    def modify_student(self):
        print('---------- 修改学生信息 ----------')
        modify_name = input('请输入要修改的学生姓名:').strip()
        if not modify_name:
            print('姓名不能为空!')
            return
        for stu in self.student_list:
            if stu.name == modify_name:
                print('请输入新的信息(留空表示不修改):')
                new_name = input(f'姓名(当前:{stu.name}):').strip()
                new_chinese = input(f'语文成绩(当前:{stu.chinese}):').strip()
                new_math = input(f'数学成绩(当前:{stu.math}):').strip()
                new_english = input(f'英语成绩(当前:{stu.english}):').strip()

                if new_name:
                    # 检查新姓名是否已存在
                    if any(s.name == new_name for s in self.student_list):
                        print(f'姓名"{new_name}"已存在,无法修改!')
                        return
                    stu.name = new_name
                if new_chinese:
                    try:
                        stu.chinese = int(new_chinese)
                    except ValueError:
                        print('语文成绩必须是整数,修改失败!')
                        return
                if new_math:
                    try:
                        stu.math = int(new_math)
                    except ValueError:
                        print('数学成绩必须是整数,修改失败!')
                        return
                if new_english:
                    try:
                        stu.english = int(new_english)
                    except ValueError:
                        print('英语成绩必须是整数,修改失败!')
                        return
                # 重新计算总分
                stu.total = stu.chinese + stu.math + stu.english
                print(f'学生"{stu.name}"的信息已更新。')
                return
        print(f'未找到名为"{modify_name}"的学生。')

    # 保存学生信息
    def save_student(self):
        print('---------- 保存学生信息 ----------')
        try:
            with open('students.json', 'w', encoding='utf-8') as f:
                # 将学生对象转换为字典列表
                final_stu_list = [{
                    "name": stu.name,
                    "chinese": stu.chinese,
                    "math": stu.math,
                    "english": stu.english,
                    "total": stu.total
                } for stu in self.student_list]
                # 写入JSON文件
                json.dump(final_stu_list, f, ensure_ascii=False, indent=4)
            print('学生信息已保存到"students.json"。')
        except Exception as e:
            print(f'保存失败:{e}')

调试步骤

  1. 设置断点

    • 打开manager.py文件。
    • add_student方法的关键代码行(例如,self.student_list.append(student))设置断点。
  2. 启动调试

    • 右键点击manager.py文件,选择“Debug 'manager'”。
    • 程序将运行并在断点处暂停。
  3. 单步执行

    • 使用F8(Step Over)逐行执行代码,观察变量self.student_list的变化。
    • 使用F7(Step Into)进入Student类的__init__方法,检查对象的初始化过程。
  4. 监视变量

    • 在“Watches”面板中添加student变量,实时查看新添加学生的信息。
  5. 计算表达式

    • 在“Evaluate Expression”窗口中输入student.total,验证总分的计算是否正确。
  6. 查看内联调试

    • 启用内联调试后,变量的值将在代码旁边直接显示,便于快速识别问题。

调试实例

假设在添加学生信息时,用户输入的数学成绩为非整数,导致程序崩溃。通过调试,您可以:

  1. 设置断点math = int(input('请输入数学成绩:'))这一行。
  2. 运行调试会话,输入非整数值,如abc
  3. 程序在断点处暂停,观察输入值和转换过程。
  4. 发现异常,通过变量监视和表达式计算,确认错误发生的位置。
  5. 修改代码,添加异常处理或输入验证,避免程序崩溃。

四、实战案例:调试学生信息管理系统

让我们通过一个具体的案例,应用所学的调试技巧,优化学生信息管理系统的代码。

问题描述

在当前的modify_student方法中,当用户尝试修改学生的成绩时,如果输入非整数值,程序将抛出ValueError,导致程序崩溃。我们需要通过调试,定位问题并优化代码。

调试步骤

  1. 设置断点

    • modify_student方法中,设置断点在stu.chinese = int(new_chinese)这一行。
  2. 启动调试

    • 运行调试会话,选择修改学生信息功能。
  3. 输入测试数据

    • 输入存在的学生姓名。
    • 在语文成绩输入框中,输入非整数值(如ninety)。
  4. 观察异常

    • 程序在尝试将new_chinese转换为整数时抛出ValueError
    • 调试器会在断点处暂停,显示异常信息。
  5. 分析问题

    • 发现输入验证不完善,未处理非整数输入的情况。
  6. 修改代码

    • modify_student方法中,添加更详细的异常处理,提示用户输入有效的成绩。

优化后的代码

# 修改学生信息
def modify_student(self):
    print('---------- 修改学生信息 ----------')
    modify_name = input('请输入要修改的学生姓名:').strip()
    if not modify_name:
        print('姓名不能为空!')
        return
    for stu in self.student_list:
        if stu.name == modify_name:
            print('请输入新的信息(留空表示不修改):')
            new_name = input(f'姓名(当前:{stu.name}):').strip()
            new_chinese = input(f'语文成绩(当前:{stu.chinese}):').strip()
            new_math = input(f'数学成绩(当前:{stu.math}):').strip()
            new_english = input(f'英语成绩(当前:{stu.english}):').strip()

            if new_name:
                # 检查新姓名是否已存在
                if any(s.name == new_name for s in self.student_list):
                    print(f'姓名"{new_name}"已存在,无法修改!')
                    return
                stu.name = new_name
            if new_chinese:
                try:
                    stu.chinese = int(new_chinese)
                except ValueError:
                    print('语文成绩必须是整数,修改失败!')
                    return
            if new_math:
                try:
                    stu.math = int(new_math)
                except ValueError:
                    print('数学成绩必须是整数,修改失败!')
                    return
            if new_english:
                try:
                    stu.english = int(new_english)
                except ValueError:
                    print('英语成绩必须是整数,修改失败!')
                    return
            # 重新计算总分
            stu.total = stu.chinese + stu.math + stu.english
            print(f'学生"{stu.name}"的信息已更新。')
            return
    print(f'未找到名为"{modify_name}"的学生。')

调试结果

  1. 重新运行调试会话,选择修改学生信息功能。
  2. 输入非整数值,程序捕获ValueError,提示用户输入有效的成绩,而不是崩溃。
  3. 确保程序稳定,提高用户体验。

总结

在本篇博客中,我们深入探讨了断点调试在Python编程中的应用:

  • 了解调试:认识到调试在编程过程中的重要性,理解其基本原理和功能。
  • 启动调试模式:学习如何在PyCharm中设置断点并启动调试会话,了解调试工具窗口的各个部分。
  • 详细调试技巧
    • 单步调试:通过逐行执行代码,深入理解程序的执行流程。
    • 监视变量:实时查看和监控变量的值,发现变量状态的变化。
    • 内联调试:在编辑器中直接显示变量的值,便于快速识别问题。
    • 计算表达式:在调试过程中手动计算和验证表达式的结果。
    • 常用快捷键:掌握调试过程中的快捷键,提高调试效率。
  • 实战案例:通过调试学生信息管理系统的实际案例,应用所学的调试技巧,优化代码并解决问题。

通过掌握断点调试,亦菲彦祖们将能够更高效地编写和维护Python程序,迅速定位和修复代码中的错误,提升整体编程能力和代码质量。

继续前行

在未来的学习中,我们将继续探索Python编程中的其他高级主题,如面向对象编程的高级特性多线程与并发网络编程等,帮助您全面提升编程技能,构建更复杂和强大的应用程序。

保持学习的热情,多动手实践,相信亦菲彦祖们一定能在编程的道路上越走越远,成为Python编程的高手!

祝学习愉快,编程顺利!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

杨胜增

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值