AIGC标识 十二、错误 和 异常

一、异常介绍

Python 是一门解释型语言,只有在程序运行后才会执行语法检查。所以,只有在运行或测试程序时,才会真正知道该程序能不能正常运行。

Python 有两种错误很容易辨认:语法错误和异常。

1.1、语法错误

程序解析时遇到的错误。

例如以下程序,因缺少 : 而出现语法错误。

while True print(1)
#     while True print(1)
#                ^^^^^
# SyntaxError: invalid syntax

1.2、异常

Python 程序的语法是正确的,在运行它的时候,也有可能发生错误。运行期检测到的错误被称为异常。

例如以下程序,因变量名未找到而引发 NameError

print(var1)
#    print(var1)
#    ^^^^
# NameError: name 'var1' is not defined. Did you mean: 'vars'?

大多数的异常都不会被程序处理,都以错误信息的形式打印出来,错误信息的前面部分显示了异常发生的上下文,并以调用栈的形式显示具体信息。



二、异常处理

对异常进行处理并不是将错误规避了,而是当程序运行的时候,出现错误的时候提供解决方案,不终止程序,可以让程序继续执行。

2.1、try except

可以使用 try except 语句来捕获异常并处理。

2.1.1、语法
try:
    可能发生异常的代码
	
except:
    异常处理的代码
    # 如果没有发生异常,程序会忽略 except 中的代码,继续向下执行。
    # 如果发生了异常,会忽略 try 中剩余代码,执行 except 中的代码。

2.1.2、案例
try:
    result = 3 / 1
    print("没有发生异常")
	
except:
    print("发生异常了")
	
print("End")



2.2、捕获指定类型的异常以及获取异常描述信息

在打印出来的异常信息中,冒号之前是异常类型,冒号之后是异常描述信息。

# NameError: name 'a' is not defined
# NameError: 冒号之前是异常类型
# : name 'a' is not defined 冒号之后是异常描述信息

如果出现的异常不是我们指定的类型中的其中一个,我们在程序中想对不同类型的异常进行不同的处理,并在处理异常的时候,要获取异常信息,我们可以通过如下方式。

2.2.1、语法
try:
    可能发生异常的代码
	
except 异常类型1 as 变量名1:
    异常处理的代码
	
except 异常类型2 as 变量名2:
    异常处理的代码
	
except (异常类型3, 异常类型4, 异常类型5) as 变量名3:
    异常处理的代码
	
except:
    异常处理的代码

  • 如果没有发生异常,程序会忽略 except 中的代码,继续向下执行。

  • 如果发生了异常,会忽略 try 中剩余代码,根据异常类型匹配到相应的 except 并执行其中的代码。

  • 如果发生了异常,且异常类型无法和任何 except 匹配,异常将向外传递。

  • 一个 except 可以同时处理多个异常,将这些异常放在一个元组中。

  • 最后一个 except 可以忽略异常类型,它将被作为通配符使用。

2.2.2、案例
try:
    result = 3 / 0
    print("发生异常了")
	
except ZeroDivisionError as e:
    print(e)
	
except (RuntimeError, TypeError, NameError) as e:
    print(e)
	
except:
    print("发生了其他异常")



2.3、else

可选地将 else 放在所有 except 之后。如果 try 中代码没有发生异常,将执行 else 中的代码。

2.3.1、语法
try:
    # 可能发生异常的代码
	
except 异常类型1 as 变量名1:
    # 异常处理的代码
	
except 异常类型2 as 变量名2:
    # 异常处理的代码
	
else:
    # 没有异常时执行的代码

2.3.2、说明
  • 从执行效果上说,将代码放到 else 块和直接放到 try 块中是一样的。

  • 将 try 正常执行完毕而没有引发任何异常后被执行的代码放到 else 中。提供了一种清晰的逻辑区分,将正常情况的代码与异常处理代码分开,使代码更易于理解和维护,有助于代码的可读性和可维护性。

  • 例如,你希望在 try 块中有些操作执行成功后,再执行其它代码,那就可以把代码放到 else 语句块中。

2.3.2、案例
try:
    result = x / y
	
except ZeroDivisionError:
    print("除数不能为零!")
	
else:
    print(f"结果是:{result}")



2.4、finally

可选地,放在最后。无论是否发生异常都会执行的代码,通常用于执行一些必须要进行的清理操作,例如关闭文件、释放资源(如网络连接、数据库连接、锁等),即使在执行 try 块中的代码时出现了异常,也能保证这些操作得以完成。

2.4.1、语法
try:
    # 可能发生异常的代码
	
except 异常类型1 as 变量名1:
    # 异常处理的代码
	
except 异常类型2 as 变量名2:
    # 异常处理的代码
	
else:
    # 没有异常时执行的代码
	
finally:
    # 无论是否发生异常都会执行的代码

2.4.2、说明
  • 如果从执行效果上说,大部分场景,将代码放到 finally 语句和放到 try-except 块外效果是一样的。

  • finally 语句是 try-except 结构的一部分,它确保了无论 try 块中是否发生异常,无论 except 块是否被执行,其中的代码都会被执行。

  • 直接放在 try-except 结构外面的代码只会在 try-except 结构正常执行完毕后才会执行,如果在 try 块中出现异常且没有被 except 块捕获,或者在 except 块中出现了新的异常导致程序终止,那么这部分代码将不会被执行。

2.4.3、案例
# try:
#     result = 3 / 0
# except ZeroDivisionError as e:
#     print(e)
# else:
#     print(result)
# finally:
#     print("finally")
# print("End")
#输出结果:
# division by zero
# finally
# End

try:
    result = 3 / 0

except NameError as e:
    print(e)

else:
    print(result)

finally:
    print("finally")

print("End")

#输出结果:
# finally
# Traceback (most recent call last):
#   File "e:\Hello\hello.py", line 15, in <module>
#     result = 3 / 0
#              ~~^~~
# ZeroDivisionError: division by zero



三、抛出

3.1、raise

当你想要在代码中明确表示发生了错误或异常情况时,可以使用 raise 来抛出异常。这可以帮助你在满足某些条件时停止程序的正常执行,并将控制权转移到异常处理部分。

3.1.1、语法
raise 异常类型("异常描述")
3.1.2、案例
def int_add(x, y):
    if isinstance(x, int) and isinstance(y, int):
        return x + y	
    else:
        raise TypeError("参数类型错误")


print(int_add(1, 2))      # 3

print(int_add("1", "2"))  # TypeError: 参数类型错误

3.2、assert 断言

assert 用于判断一个表达式,在表达式条件为 False 的时候触发异常,常用于调试程序。

3.2.1、语法
assert 表达式 [, 异常描述]

等价于:

if not 表达式:
    raise AssertionError([异常描述])

3.2.2、案例
def int_add(x, y):
    assert isinstance(x, int) and isinstance(y, int), "参数类型错误"
    return x + y


print(int_add(1, 2))  # 3

print(int_add("1", "2"))  # AssertionError: 参数类型错误



四、自定义异常

通过直接或者间接继承 Exception 类来创建自己的异常。例如:

class MyError(Exception):

    def __init__(self, value):
        self.value = value

    def __str__(self):
        return repr(self.value)


try:
    raise MyError(1)
except MyError as e:
    print("触发自定义异常:", e.value)



五、异常的传递

当存在 try 嵌套或函数嵌套时,若内层出现了异常且在内层无法处理,会将异常一层一层向外传递,直到异常被处理或程序报错。

try:
    try:
        print(1 / 0)
    except NameError as e:
        print("第三层", e)
		
    except TypeError as e:
        print("第二层", e)
		
except Exception as e:
    print("第一层", type(e), e)
	
# 第一层 <class 'ZeroDivisionError'> division by zero



六、with 关键字

Python 中的 with 语句用于异常处理,封装了 try except finally 编码范式,提供了一种简洁的方式来确保资源的正确获取和释放,同时处理可能发生的异常,提高了易用性。使代码更清晰、更具可读性,简化了文件流等公共资源的管理。

6.1、语法

with expression as variable:
    # 代码块
  • expression:通常是一个对象或函数调用,该对象需要是一个上下文管理器,即实现了 enterexit 方法。

  • variable:是可选的,用于存储 expression 的 enter 方法的返回值。

6.2、工作原理

  • 使用 with 关键字系统会自动调用 f.close() 方法,with 的作用等效于 try finally 语句。

  • 当执行 with 语句时,会调用 expression 对象的 enter 方法。

  • enter 方法的返回值可以被存储在 variable 中(如果有),以供 with 代码块中使用。

  • 然后执行 with 语句内部的代码块。

  • 无论在代码块中是否发生异常,都会调用 expression 对象的 exit 方法,以确保资源的释放或清理工作,这类似于 try-except-finally 中的 finally 子句。

6.3、案例:打开一个文件并向其中写入内容,验证出现异常后文件是否正常关闭。

6.3.1、常规方式
try:
    file = open("test.txt", "w")
    file.write(a)
    file.close()
finally:
    print("文件是否关闭:", file.closed)  # 文件是否关闭: False
6.3.2、使用 try finally
try:

    file = open("test.txt", "w")
	
    try:
        file.write(a)
    finally:
        file.close()
finally:
    print("文件是否关闭:", file.closed)  # 文件是否关闭: True

6.3.3、使用 with
try:
    with open("test.txt", "w") as f:
        f.write(a)
finally:
    print("文件是否关闭:", f.closed)  # 文件是否关闭: True



七、Python 常见异常

7.1、异常基类

异常 说明
BaseException 所有内置异常的基类。它不应该被用户自定义类直接继承(这种情况请使用 Exception)。
Exception 所有内置的非系统退出类异常都派生自此类。所有用户自定义异常也应当派生自此类。
ArithmeticError 此基类用于派生针对各种算术类错误而引发的内置异常:OverflowError, ZeroDivisionError, FloatingPointError
BufferError 当与缓冲区相关的操作无法执行时将被引发。
LookupError 此基类用于派生当映射或序列所使用的键或索引无效时引发的异常:IndexError, KeyError。这可以通过 codecs.lookup() 来直接引发。

7.2、异常基类

异常 说明
AssertionError assert 语句失败时将被引发。
AttributeError 当属性引用或赋值失败时将被引发。
IndexError 当序列抽取超出范围时将被引发。
KeyError 当在现有键集合中找不到指定的映射(字典)键时将被引发。
KeyboardInterrupt 当用户按下中断键(通常为 Control-C 或 Delete)时将被引发。
MemoryError 当一个操作耗尽内存但情况仍可(通过删除一些对象)进行挽救时将被引发。
NameError 当某个局部或全局名称未找到时将被引发。
OSError 此异常在一个系统函数返回系统相关的错误时将被引发,此类错误通常由系统调用引发。
posted @ 2026-08-08 20:00  jock_javaEE  阅读(3)  评论(0)    收藏  举报