跳至主要內容

异常及调试

Sankgao约 2613 字大约 9 分钟LanguagesPython

异常处理

在程序运行过程中,经常会遇到各种各样的错误,这些错误统称为 异常。这些异常有的是由于开发者将关键字敲错导致的,这类错误多数产生的是 SyntaxError:invalid syntax(无效的语法),这些直接导致程序不能运行。

程序运行出错时会有 Traceback 信息,Traceback 指的是 异常堆栈信息,描述了程序运行的过程及引发异常的信息。

Python 中常见的异常

异常描述
NameError尝试访问一个没有声明的变量引发的错误
IndexError索引超出序列范围引发的错误
IndentationError缩进错误
ValueError传入的值错误
KeyError请求一个不存在的字典关键字引发的错误
IOError输入输出错误(如要读取的文件不存在)
ImportError当 import 语句无法找到模块或 from 无法在模块中找到相应的名称时引发的错误
AttributeError尝试访问未知的对象属性引发的错误
TypeError类型不合适引发的错误
MemoryError内存不足
ZeroDivisionError除数为 0 引发的错误

异常处理语句

try···except 语句

在使用时,把可能产生异常的代码放在 try 语句块中,把处理结果放在 except 语句块中,如果 try 语句块中的代码出现错误时,就会执行 except 语句块中的代码;如果 try 语句块中的代码没有错误,就不执行 except 语句块中的代码。

语法格式:

try:
    执行的代码 1
except [ExceptionName [as alias]]:
    执行的代码 2
  • 执行的代码 1:指定可能出现错误的代码块
  • [ExceptionName [as alias]]:可选参数,指定要捕获的异常。ExceptionName 表示要捕获异常的名称;al alias 表示为捕获异常的名称指定一个别名,通过该别名,可以记录异常的具体内容
  • 执行的代码 2:指定出现异常时,执行的代码

try···except 语句流程:

提示

except 后面不指定异常名称,则表示捕获全部异常。通过 try···except 语句捕获到异常后,程序会继续执行

例如:

def division():
    """
    功能:分苹果
    :return:
    """
    print(('-' * 20), "分苹果了", ('-' * 20))
    apple = int(input("请输入苹果的个数:"))
    children = int(input("请输入来了几个下朋友:"))
    result = apple // children
    remain = apple % children

    if remain > 0:
        print(f"{apple} 个苹果,平均分给 {children} 个小朋友,每个人分 {result} 个,剩下 {remain} 个苹果")
    else:
        print(f"{apple} 个苹果,平均分给 {children} 个小朋友,每个人分 {result} 个苹果")


if __name__ == '__main__':
    try:
        division()
    except ZeroDivisionError:
        print("出错了!苹果不能被 0 个小朋友分")


# 多个 except 语句
if __name__ == '__main__':
    try:
        division()
    except ZeroDivisionError:
        print("出错了!苹果不能被 0 个小朋友分")
    except ValueError as e:
        print("输入错误:", e)


# 同时处理多个类似异常,可以将 except 语句进行合并处理
if __name__ == '__main__':
    try:
        division()
    except (ZeroDivisionError, ValueError) as e:
        print("出错了!原因是:", e)

try···except···else 语句

try 语句块中没有发现异常时要执行的语句,当 try 语句块中发现异常,将不执行。

语法格式:

try:
    执行的代码 1
except [ExceptionName [as alias]]:
    执行的代码 2
else:
    执行的代码 3

try···except 语句流程:

提示

依赖于 try 代码块成功执行的代码都应放到 else 代码块中

例如:

def division():
    """
    功能:分苹果
    :return:
    """
    print(('-' * 20), "分苹果了", ('-' * 20))
    apple = int(input("请输入苹果的个数:"))
    children = int(input("请输入来了几个下朋友:"))
    result = apple // children
    remain = apple - result * children

    if remain > 0:
        print(f"{apple} 个苹果,平均分给 {children} 个小朋友,每个人分 {result} 个,剩下 {remain} 个苹果")
    else:
        print(f"{apple} 个苹果,平均分给 {children} 个小朋友,每个人分 {result} 个苹果")


if __name__ == '__main__':
    try:
        division()
    except ZeroDivisionError:
        print("出错了!苹果不能被 0 个小朋友分")
    except ValueError as e:
        print("输入错误:", e)
    else:
        print("苹果顺利分完!")

try···except···finally 语句

有时在 try···except 语句中会占用一些资源。例如:打开的文件、网络连接、打开的数据库等。如果要释放这些资源,就要用 finally 进行清理代码。

完整的异常处理语句应该包含 finally 代码块,通常情况下无论程序中有无异常产生,finally 代码块都会执行。

语法格式:

try:
    执行的代码 1
except [ExceptionName [as alias]]:
    执行的代码 2
else:
    执行的代码 3
finally:
    执行的代码 4

try···except···finally 语句流程:

例如:

def division():
    """
    功能:分苹果
    :return:
    """
    print(('-' * 20), "分苹果了", ('-' * 20))
    apple = int(input("请输入苹果的个数:"))
    children = int(input("请输入来了几个下朋友:"))
    result = apple // children
    remain = apple - result * children

    if remain > 0:
        print(f"{apple} 个苹果,平均分给 {children} 个小朋友,每个人分 {result} 个,剩下 {remain} 个苹果")
    else:
        print(f"{apple} 个苹果,平均分给 {children} 个小朋友,每个人分 {result} 个苹果")


if __name__ == '__main__':
    try:
        division()
    except ZeroDivisionError:
        print("出错了!苹果不能被 0 个小朋友分")
    except ValueError as e:
        print("输入错误:", e)
    else:
        print("苹果顺利分完!")
    finally:
        print("进行分苹果操作")

使用 raise 语句抛出异常

如果某个函数或方法可能会产生异常,但不想在当前函数或方法中处理这个异常,则可以使用 raise 语句在函数或方法中抛出异常。

语法格式raise [ExceptionName [(reason)]]

  • ExceptionName:可选参数,指定抛出异常名称以及异常信息的相关描述,如果省略将错误原样抛出
  • (reason):可选参数,指定抛出异常时附带相关描述信息,如果省略将不附带相关描述信息

例如:

def division():
    """
    功能:分苹果
    :return:
    """
    print(('-' * 20), "分苹果了", ('-' * 20))
    apple = int(input("请输入苹果的个数:"))
    children = int(input("请输入来了几个下朋友:"))

    if apple < children:
        raise ValueError("苹果太少了,不够分!")

    result = apple // children
    remain = apple - result * children

    if remain > 0:
        print(f"{apple} 个苹果,平均分给 {children} 个小朋友,每个人分 {result} 个,剩下 {remain} 个苹果")
    else:
        print(f"{apple} 个苹果,平均分给 {children} 个小朋友,每个人分 {result} 个苹果")


if __name__ == '__main__':
    try:
        division()
    except ZeroDivisionError:
        print("出错了!苹果不能被 0 个小朋友分")
    except ValueError as e:
        print("输入错误:", e)
    else:
        print("苹果顺利分完!")
    finally:
        print("进行分苹果操作")

程序调试

在程序开发过程中,有语法和逻辑方面的错误。当语法错误时,程序会直接停止;当逻辑错误时,程序会一直执行但结果是错误的。这时需要掌握一定的程序调试方法。

使用自带的 IDLE 进行程序调试

在继承开发工具中打开 Debug 功能,然后添加需要的 断点,按下 F5 继续执行程序。

断点的作用

设置断点后,程序执行到断点时就会暂时中断执行,可以查看某些变量的值,程序可以随时继续执行

使用 assert 语句调试程序

Python 提供了 assert 语句调试程序,assert 意思是 断言,一般用于对程序某个时刻必须满足的条件进行验证。

语法格式assert expression [, reason]

  • expression:条件表达式,如果该表达式的值为真时,什么都不做;如果为假时,则抛出 AssertionError 异常
  • reason:可选参数,用于对判断条件进行描述

例如:

def division():
    """
    功能:分苹果
    :return:
    """
    print(('-' * 20), "分苹果了", ('-' * 20))
    apple = int(input("请输入苹果的个数:"))
    children = int(input("请输入来了几个下朋友:"))

    # 断言调试
    assert apple > children, "苹果不够分!"

    result = apple // children
    remain = apple - result * children

    if remain > 0:
        print(f"{apple} 个苹果,平均分给 {children} 个小朋友,每个人分 {result} 个,剩下 {remain} 个苹果")
    else:
        print(f"{apple} 个苹果,平均分给 {children} 个小朋友,每个人分 {result} 个苹果")


if __name__ == '__main__':
    try:
        division()
    except ZeroDivisionError:
        print("出错了!苹果不能被 0 个小朋友分")
    except ValueError as e:
        print("输入错误:", e)
    else:
        print("苹果顺利分完!")
    finally:
        print("进行分苹果操作")

通常情况下,assert 语句可以和异常处理语句结合使用。例如:

if __name__ == '__main__':
    try:
        division()
    except ZeroDivisionError:
        print("出错了!苹果不能被 0 个小朋友分")
    except ValueError as e:
        print("输入错误:", e)
    # assert 和异常处理语句结合使用
    except AssertionError as e:
        print("输入错误:", e)
    else:
        print("苹果顺利分完!")
    finally:
        print("进行分苹果操作")

提示

assert 语句只在调试阶段有效。可以通过在执行 python 命令时加入 -O(大写)参数来关闭 assert 语句

自定义异常类

自定义的异常类,需要继承 Exception 类或子类,之前的 ZeroDivisionErrorValueError 异常都属于 Exception 的子类。

语法格式:

class TestExcept(Exception):
    def __init__(self, message):
        super().__init__(massage)
  • TestExcept:定义异常类的名称
  • message:定义异常的描述信息
  • super():调用父类构造方法,并把参数 massage 传给父类构造方法

例如:用户输入的密码不能低于 6 位数。自定义一个异常,用于检测用户输入的密码是否符合规定,不符合则引发异常,提示当前输入的密码长度和最小密码长度不能低于 6 位数。

# 异常捕获的类
class MyError(Exception):
    # length 为用户输入的密码长度,min_len 为规定的最小长度
    def __init__(self, length, min_len):
        self.length = length
        self.min_len = min_len

    # 设置抛出异常的描述信息
    def __str__(self):
        return "您输入的长度是 %s,不能少于 %s" % (self.length, self.min_len)


def main():
    try:
        # 获取用户输入的密码
        con = input("请输入密码:")
        # 获取用户输入的密码长度
        l = len(con)

        if l < 6:
            # 长度低于设定的 6 位数则引发异常
            raise MyError(l, 6)
        
        # assert l >= 6, MyError(l, 6)
    
    # 有错误则提示
    except Exception as ss:
        print(ss)
    else:
        # 没有错误则执行
        print("您的密码输入完毕")


main()