【python】随笔 - 知识点小课堂 -11.异常处理结构和单元测试

发布于:2024-05-24 ⋅ 阅读:(37) ⋅ 点赞:(0)

前言

本文是关于异常处理结构和单元测试的习题整理和讲解

1~4

异常处理笔记

1. 主动抛出异常
  • 在 Python 中,可以使用 raise 语句主动抛出一个异常。这可以在代码检测到错误条件时用于中断正常的程序流程。
if some_condition:
    raise ValueError("An error occurred")
2. try...except 结构中的 else 子句
  • try...except 异常处理结构中,如果 try 块中的代码抛出了异常,那么 else 块中的代码将不会执行。
  • else 块只有在 try 块中没有发生任何异常时才会运行。
try:
    # 尝试执行的代码
except:
    # 异常处理代码
else:
    # 如果没有异常发生,执行这里的代码
3. try...except 结构中的 except 子句
  • except 子句用于捕捉 try 块中可能出现的特定类型的异常,以便可以对其进行处理。
try:
    # 尝试执行的代码
except SomeException:
    # 处理 SomeException 异常的代码
4. 无论是否发生异常都要执行的代码
  • 如果有一段代码无论是否发生异常都需要执行,那么应该将它放在 finally 子句中。finally 块中的代码总是会被执行,无论 try 块中是否抛出异常。
try:
    # 尝试执行的代码
except:
    # 异常处理代码
finally:
    # 无论是否发生异常,都会执行这里的代码

11.5

下面代码的运行结果为

修正后的代码如下:

def func():
    for i in range(10):
        if i > 3:
            return i
        yield i

r = func()
for i in range(10):
    try:
        next(r)
    except StopIteration as e:
        print(e.value)
        break
  1. func() 是一个生成器函数,它使用 yield 语句产生值。当 i 大于 3 时,函数会返回 i 并结束。

  2. r = func() 调用 func() 函数,创建了一个生成器对象 r

  3. 外层的 for 循环尝试 10 次通过调用 next(r) 来从生成器中获取下一个值。

  4. try 块中的 next(r) 尝试从生成器 r 获取下一个值。

  5. 如果生成器已经没有更多的值可以产生(即当 StopIteration 异常被抛出时),except 块会捕获这个异常并打印出 e.value,然后通过 break 语句退出外层的 for 循环。

生成器 func() 会在 i 等于 4 时返回,因为 i > 3 的条件为真,此时 func() 函数结束,r 将不再产生任何值。因此,next(r) 在第一次迭代时会成功获取 0,在第二次迭代获取 1,在第三次迭代获取 2,在第四次迭代获取 3,然后在第五次迭代时 i 等于 4,生成器返回,抛出 StopIteration 异常。

StopIteration 异常被捕获,其值(也就是生成器返回的最后一个 yield 表达式的值,这里是 4)被打印出来,然后循环终止。所以,程序的输出将是:

4

11.6~11.8

断言操作笔记

1. 断言(assert)的使用
  • assert 是 Python 中的一个关键字,用于调试目的,检查条件是否为真。如果条件为假(即评估结果为 False),则会引发 AssertionError 异常。
assert 3 == 3, "3 does not equal 3"
  • 如果 3 == 3 为真,程序会继续执行。如果为假,将抛出 AssertionError,并显示消息 “3 does not equal 3”。
2. 断言引发异常的条件
  • 使用 assert 语句时,如果表达式的结果为 False,则会引发 AssertionError 异常。通常用于在代码中设置检查点,确保某些条件在程序执行时满足。
3. 断言的禁用
  • 可以通过 Python 命令行界面的 -O(optimize)选项或在代码中使用 optimizations 模块的 assert 函数来禁用断言。禁用断言后,所有 assert 语句将不会被执行。
import dis
dis.dis(compile('assert True', '', 'exec'))
  • 在优化模式下,assert 语句会被编译器忽略。

其他表达式与异常

11.6 assert 3 == 3
  • 这个断言表达式是 True,所以它不会引发异常。
11.7 3 / 0
  • 这个表达式会尝试除以零,这在数学和编程中都是未定义的操作,因此会引发 ZeroDivisionError 异常。
11.8 'a' + 1
  • 这个表达式尝试将字符串与整数相加,这在 Python 中是不允许的,因为这两种类型的数据不能进行加法操作,所以会引发 TypeError 异常。

11.9~11.12

异常处理笔记

1. ZeroDivisionError 异常
  • 尝试进行除以零的操作时,会抛出 ZeroDivisionError 异常。
2. TypeError 异常
  • 尝试进行类型不兼容的操作时,会抛出 TypeError 异常。
  • 表达式 '2' + 1 会引发 TypeError,因为不能将字符串与整数直接相加。
3. ValueError 异常
  • 当传入的参数不是一个合法的值时,某些函数会抛出 ValueError 异常。
  • 表达式 int('3.14') 会引发 ValueError,因为字符串 '3.14' 包含小数点,不能直接转换为整数。
4. FileNotFoundError 异常
  • 尝试打开不存在的文件时,会抛出 FileNotFoundError 异常。
  • 使用 open() 函数打开文件时,如果文件路径错误,将引发 FileNotFoundError

11.13

判断对错:试图计算表达式’Python xiaowu’.encode().decode(‘gbk’)
时会抛出 UnicodeDecodeError 异常并提示无法解码。

判断:错误

原因:

  • 表达式 'Python xiaowu'.encode().decode('gbk') 首先使用 encode() 方法将字符串编码为默认的 UTF-8 编码的字节序列,然后尝试使用 'gbk' 编码解码这些字节序列。
  • 'gbk' 编码是用于中文编码的一种方式,它能够处理汉字字符。
  • 由于 'Python xiaowu' 字符串中包含的汉字 “小吴” 在 'gbk' 编码中是有效的,所以这个表达式不会抛出 UnicodeDecodeError 异常。
  • 但是,如果原始字符串包含无法用 'gbk' 编码解码的字符,那么在解码时可能会抛出 UnicodeDecodeError 异常。

但,如果字符串包含非 'gbk' 编码能够表示的特殊字符,那么解码时可能会抛出异常。例如:

# 这个字符串包含一个在 GBK 中没有的字符
s = "Python xiaowu 🐍"

# 抛出 UnicodeDecodeError,因为 🐍 无法用 GBK 解码
s.encode().decode('gbk')

在这个例子中,尝试将包含蛇表情的字符串解码为 'gbk' 编码将会引发 UnicodeDecodeError,因为蛇表情不是 'gbk' 编码所能表示的字符。

11.14

判断对错:已知x=[],那么试图执行语句x.pop()时会抛出
IndexError 类型的异常。

判断:正确

原因:

  • x.pop() 方法尝试从列表 x 中移除并返回最后一个元素。
  • 在已知 x=[] 的情况下,列表 x 是空的,也就是说它没有任何元素。
  • 当尝试在一个空列表上执行 pop() 操作时,由于没有元素可以移除,Python 会抛出一个 IndexError 异常,因为索引操作超出了列表的范围。

以下是引发此异常的示例代码:

x = []
try:
    x.pop()
except IndexError:
    print("An IndexError occurred because the list is empty.")

在这个示例中,try 块尝试执行 x.pop(),但由于 x 是空的,将会引发 IndexError。然后 except 块捕获异常,并打印出相应的错误消息。

11.15

判断对错:已知 x={},那么试图执行语句 print(x[‘a’])时会抛出
KeyError 类型的异常。

判断:正确

原因:

  • x['a'] 尝试访问字典 x 中键 'a' 对应的值。
  • 在已知 x={} 的情况下,字典 x 是空的,也就是说它不包含任何键值对。
  • 当尝试访问一个不存在的键时,Python 字典会抛出一个 KeyError 异常。

引发此异常的示例代码:

x = {}
try:
    print(x['a'])
except KeyError:
    print("A KeyError occurred because 'a' is not a key in the dictionary.")

在这个示例中,try 块尝试访问字典 x 中的键 'a',但由于 x 是空的,将会引发 KeyError。然后 except 块捕获异常,并打印出相应的错误消息。

11.16

判断对错:在try…except…else…结构中,如果try块的语句引发了
异常则会执行 else 块中的代码。

判断:错误

原因:

  • try...except...else... 结构中,else 块中的代码仅在 try 块中没有发生任何异常时才会执行。
  • 如果 try 块中的代码抛出了异常,并且该异常没有在 except 块中被捕获和处理,那么 else 块中的代码将不会执行。
  • else 块通常用于放置那些只有在没有异常发生的情况下才需要执行的代码。

以下是 try...except...else... 结构的示例代码:

try:
    # 尝试执行的代码
    1 / 0
except ZeroDivisionError:
    # 如果发生 ZeroDivisionError,则执行这里的代码
    print("Caught a ZeroDivisionError")
else:
    # 如果没有异常发生,则执行这里的代码
    print("No exception occurred")

在这个示例中,try 块中的代码尝试执行除以零的操作,这将抛出 ZeroDivisionError。由于 except 块捕获了这个异常,else 块中的代码不会执行。如果没有异常发生,else 块中的代码将会执行。

11.26

判断对错:列表和元组支持很多相同的操作,二者非常类似,所以表达式[1]+(2,3)可以正常计算,不会引发异常。

判断:答案是正确但是实际上这道题不好

实际上会报错的
在这里插入图片描述
判断:错误

原因:

  • 列表(list)和元组(tuple)确实支持许多相同的操作,例如索引、切片等,但它们在某些操作上有所不同,特别是在加法操作方面。
  • 在 Python 中,列表和元组的加法操作是不同的。列表的加法操作 + 是列表连接操作,而元组的加法操作实际上是元组拼接操作。
  • 表达式 [1] + (2, 3) 尝试将一个列表和一个元组进行加法操作,这在 Python 中是不允许的,因为它们是不同的数据类型。尝试执行这个表达式会抛出 TypeError 异常。

以下是引发此异常的示例代码:

try:
    print([1] + (2, 3))
except TypeError:
    print("A TypeError occurred because you cannot add a list to a tuple.")

在这个示例中,尝试将列表 [1] 和元组 (2, 3) 相加会抛出 TypeError,然后 except 块捕获异常,并打印出相应的错误消息。正确的操作方式是将两个列表或两个元组进行加法操作。例如:

# 列表相加
list1 = [1]
list2 = [2, 3]
print(list1 + list2)  # 输出: [1, 2, 3]

# 元组相加
tuple1 = (1,)
tuple2 = (2, 3)
print(tuple1 + tuple2)  # 输出: (1, 2, 3)

在这个正确的示例中,两个列表或两个元组相加会正常工作,并且不会引发异常。

11.30

判断对错:assert断言语句执行时,如果要求的条件是成立的,直接构行后面的代码,和什么也没发生一样。

判断:正确

原因:

  • assert 是 Python 中的一个关键字,用作断言。它用于在代码中设置检查点,以确保某个条件为真(即非零或非空)。
  • 如果 assert 语句后的条件评估为 True,那么 assert 语句什么也不做,程序会继续执行 assert 语句后面的代码,就像没有发生任何特别的事情一样。
  • 如果条件评估为 Falseassert 语句将引发 AssertionError 异常。

以下是 assert 断言语句的使用示例:

assert 2 + 2 == 4, "The assertion failed!"
print("This line will be executed if the assertion is true.")

在这个示例中,因为 2 + 2 == 4 是真的,所以 assert 语句不会执行任何操作,程序将正常执行下一条语句并打印 “This line will be executed if the assertion is true.”。如果条件是假,例如:

assert 2 + 2 == 5, "The assertion failed!"

这将引发 AssertionError 异常,并显示消息 “The assertion failed!”。

11.33~11.43

在这里插入图片描述
在这里插入图片描述

print(3(4+5))
:1: SyntaxWarning: ‘int’ object is not callable; perhaps you missed a comma?
Traceback (most recent call last):
File “”, line 1, in
TypeError: ‘int’ object is not callable

上面的解释,
这个异常是Python解释器将 3(4+5) 理解为尝试调用整数 3 作为一个函数,而整数是不能被调用的

补充

在Python中,列表(list)对象确实没有 rindex 方法。rindex 方法是字符串(str)对象的方法,用于从字符串中从后向前搜索子串,并返回子串在字符串中最后一次出现的索引位置。如果找不到子串,rindex 方法会抛出一个 ValueError 异常。

index函数是针对字符串来的不是列表
在这里插入图片描述
整理报错

错误类型 描述 可能的原因
SyntaxError 语法错误,代码不符合Python的语法规则。 拼写错误、缺少括号、错误的缩进等。
NameError 使用了未定义的变量。 变量名拼写错误、未初始化变量、作用域问题等。
TypeError 发生了一个类型错误,比如对错误类型的操作。 尝试执行不支持的操作,如在字符串上调用数字类型的函数。
ValueError 传入了不合法的值。 例如,将非数字字符串传递给int()函数。
IndexError 尝试访问列表等序列类型中不存在的索引。 索引超出范围或使用负数索引时超出序列长度。
KeyError 尝试访问字典中不存在的键。 使用了字典中没有的键。
AttributeError 对对象进行了不支持的操作,比如访问不存在的属性或方法。 对象没有该属性或方法,或者拼写错误。
OSError 操作系统相关的错误,如文件操作失败。 文件不存在、没有权限、文件系统错误等。
IOError 输入输出操作失败。 无法打开文件、读写错误等。
ImportError 无法导入模块或包。 模块不存在、路径错误、环境问题等。
TimeoutError 操作超时。 网络请求超时、等待某个事件超时等。
RuntimeError 运行时错误,程序执行过程中出现的问题。 内存不足、递归深度超出限制等。
NotImplementedError 尚未实现的方法被调用。 继承自基类的某些方法在子类中没有实现。
IndentationError 缩进错误,代码的缩进不符合Python的要求。 缩进不一致、使用空格和制表符混编等。
AssertionError 断言失败。 代码中的断言表达式返回False
EOFError 到达文件末尾但预期还有更多数据。 文件读取操作中,到达文件末尾但还有未完成的输入。

11.44 Python异常处理结构的几种形式

  1. try…except:这是最基本的异常处理结构,用于捕获并处理异常。

    try:
        # 尝试执行的代码
    except ExceptionType1:
        # 如果ExceptionType1异常发生,执行这里的代码
    except (ExceptionType2, ExceptionType3) as e:
        # 如果ExceptionType2或ExceptionType3异常发生,执行这里的代码
        # 'e' 是异常对象,可以用于获取更多信息
    
  2. try…except…else:在try块中没有发生任何异常时,执行else块中的代码。

    try:
        # 尝试执行的代码
    except ExceptionType:
        # 如果发生异常,执行这里的代码
    else:
        # 如果没有异常,执行这里的代码
    
  3. try…except…finally:无论是否发生异常,finally块中的代码都会被执行。这常用于清理资源,比如关闭文件。

    try:
        # 尝试执行的代码
    except ExceptionType:
        # 如果发生异常,执行这里的代码
    finally:
        # 无论是否发生异常,都会执行这里的代码
    
  4. try…except…else…finally:结合了elsefinallyelse在没有异常时执行,而finally无论是否发生异常都会执行。

    try:
        # 尝试执行的代码
    except ExceptionType:
        # 如果发生异常,执行这里的代码
    else:
        # 如果没有异常,执行这里的代码
    finally:
        # 无论是否发生异常,都会执行这里的代码
    

主要可分为上述这四种情况

11.45

异常和错误有什么区别?
相关,但含义却有所不同。

错误(Error)

错误通常指的是程序在编译或运行时遇到的问题,这些问题阻止了程序的正常执行。错误可以是语法错误、逻辑错误、运行时错误等。错误可以分为两大类:

  1. 编译时错误:这些错误发生在代码编写阶段,通常是由于语法错误或违反了编程语言的规则。编译器或解释器在尝试将代码转换为可执行格式时会检测到这些错误,并且不会生成可执行文件。

  2. 运行时错误:这些错误发生在程序执行过程中,可能是由于程序违反了某些运行时的规则,或者遇到了无法预料的情况。例如,访问数组的非法索引、除以零、内存不足等。

异常(Exception)

异常是程序运行时发生的一种特殊类型的事件,它代表了一个不在程序正常控制流中的条件。异常通常是由于程序试图执行一个无法正常完成的操作,比如读取不存在的文件、网络连接失败等。异常提供了一种机制,允许程序在遇到这些异常情况时以一种结构化的方式进行响应。

异常处理是编程中的一种强大工具,它允许程序在遇到错误时恢复并继续执行,而不是立即崩溃。在大多数编程语言中,包括Python,异常处理通常涉及以下几个关键概念:

  • 抛出(Raise):当检测到异常条件时,程序会抛出一个异常。
  • 捕获(Catch):使用try...except块捕获并处理异常。
  • 传播(Propagate):如果异常没有被捕获,它会向上传播到调用栈的上一层,直到被捕获或者程序终止。

总结

  • 错误:指程序无法自行解决的问题。这类问题通常需要开发者介入,通过修改代码或配置来解决。
  • 异常:指程序在运行时可能会遇到的一些错误,但这些问题可以通过程序中的异常处理机制来解决。异常处理允许程序在遇到问题时采取特定的恢复措施,从而继续执行或以一种可控的方式终止。

END