Python游戏世界打怪升级之新手指引十四【异常】

异常

今天我们来学习Python里面的异常,什么是异常?下面给出了详细的解释

异常的基本概念

  • 什么是异常?
    • 异常是程序运行时发生的错误,会中断程序的正常执行。
    • 例如:除以零、访问不存在的文件、类型错误等。
  • 异常的作用
    • 帮助开发者捕获和处理错误,避免程序崩溃。
    • 提高代码的健壮性和可维护性。

主要从以下几个方面来学习异常

  • 常见异常类型

    • ZeroDivisionError:除零错误。
    • FileNotFoundError:文件未找到。
    • TypeError:类型错误。
    • ValueError:值错误。
    • IndexError:索引错误。
    • KeyError:字典键错误。
    • AttributeError:属性错误。
    • ImportError:导入模块错误
  • 异常处理try...except...else...finally

  • 触发异常raise

  • 异常链

  • 自定义异常

  • 清理操作finally

Python中的异常处理

基本语法

try:
    # 可能引发异常的代码
except 异常类型 as 变量:
    # 异常处理代码
else:
    # 如果没有异常发生,执行此代码
finally:
    # 无论是否发生异常,都会执行此代码

异常处理try...except...

可以通过下面的例子来看一下捕获除零错误

try:
    result = 10 / 0
except ZeroDivisionError as e:
    print(f"Error: {e}") # Error: division by zero
else:
    print(f"Result: {result}")
finally:
    print("Execution complete.") # Execution complete.

工作原理如下

  • 执行try后面的语句,没有触发异常,则跳过except,执行后面的else语句;
  • finally语句则是不管有没有触发异常都会执行
  • 发生了异常,则跳过try语句后面的内容,判断异常类型和except后指定的类型是否相匹配,匹配则执行except子句

相比大家也发现了except子句,as 后面指定了一个变量,这里要讲解以下

这个变量的作用就是被绑定到异常实例,该实例通常会有一个存储参数的args属性

还有一个raise,将在下面讲到

try:
    raise Exception('spam', 'eggs')
except Exception as inst:
    print(type(inst))    # 异常的类型<class 'Exception'>
    print(inst.args)     # 参数保存在 .args 中('spam', 'eggs')
    print(inst)          # __str__ 允许 args 被直接打印,
                         # 但可能在异常子类中被覆盖('spam', 'eggs')
    x, y = inst.args     # 解包 args
    print('x =', x) 	 # x = spam
    print('y =', y) 	 # y = eggs
    
    
<class 'Exception'>
('spam', 'eggs')
('spam', 'eggs')
x = spam
y = eggs

触发异常raise

作用,强制触发指定的异常;唯一的参数就是要触发的异常,这个参数必须是异常实例、异常类

def validate_age(age):
    if age < 0:
        raise ValueError("Age cannot be negative.")
    elif age < 18:
        raise ValueError("You must be at least 18 years old.")
    else:
        print("Age is valid.")

try:
    validate_age(-5)
except ValueError as e:
    print(f"Error: {e}")  # Error: Age cannot be negative.

异常链

如果一个未处理的异常发生在except部份内,将会有被处理的异常附加到它上面

通俗点就是说捕获异常后,可以抛出新的异常,同时保留原始异常的信息


try:
    10 / 0
except ZeroDivisionError as e:
    raise ValueError("An error occurred while dividing.") from e


"""
Traceback (most recent call last):
  File "D:\Code_Study\Python_world\13.py", line 45, in <module>
    10 / 0
    ~~~^~~
ZeroDivisionError: division by zero

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "D:\Code_Study\Python_world\13.py", line 47, in <module>
    raise ValueError("An error occurred while dividing.") from e
ValueError: An error occurred while dividing.
"""



# 使用from None 表达禁用异常链
try:
    10 / 0
except ZeroDivisionError:
    raise ValueError("An error occurred while dividing.") from None

"""
Traceback (most recent call last):
  File "D:\Code_Study\Python_world\13.py", line 69, in <module>
    raise ValueError("An error occurred while dividing.") from None
ValueError: An error occurred while dividing.

"""

自定义异常

为什么需要自定义异常,就是因为内置异常无法满足特定业务需求的时候,可以自定义异常

怎么自定义呢?请看下面的例子

  • 创建新的异常类为自己的异常
  • 异常都应该继承Exception
class MyCustomError(Exception):
    def __init__(self, message):
        self.message = message
        super().__init__(self.message)

try:
    raise MyCustomError("This is a custom error.")
except MyCustomError as e:
    print(f"Custom error caught: {e}")

清理操作finally

finally不是必须的,但是如果它存在的话,就表示不管是否触发异常都要执行finally语句

try:
    raise KeyboardInterrupt
finally:
    print('Goodbye, world!')

"""
Traceback (most recent call last):
  File "D:\Code_Study\Python_world\13.py", line 82, in <module>
    raise KeyboardInterrupt
KeyboardInterrupt
Goodbye, world!
"""

需要注意以下几个复杂的情况

  • 如果执行 try 子句期间触发了某个异常,则某个 except 子句应处理该异常。如果该异常没有 except 子句处理,在 finally 子句执行后会被重新触发
  • exceptelse 子句执行期间也会触发异常。 同样,该异常会在 finally 子句执行之后被重新触发。
  • 如果 finally 子句中包含 breakcontinuereturn 等语句,异常将不会被重新引发
  • 如果执行 try 语句时遇到 break,、continuereturn 语句,则 finally 子句在执行 breakcontinuereturn 语句之前执行
  • 如果 finally 子句中包含 return 语句,则返回值来自 finally 子句的某个 return 语句的返回值,而不是来自 try 子句的 return 语句的返回值。

下面来看一个具体的例子

def divide(x, y):
    try:
        result = x / y
    except ZeroDivisionError:
        print("division by zero!")
    else:
        print("result is", result)
    finally:
        print("executing finally clause")

divide(2, 1)
"""
result is 2.0
executing finally clause
"""

divide(2, 0)
"""
division by zero!
executing finally clause
"""

divide("2", "1")
"""
executing finally clause
Traceback (most recent call last):
  File "D:\Code_Study\Python_world\13.py", line 104, in <module>
    divide("2", "1")
    ~~~~~~^^^^^^^^^^
  File "D:\Code_Study\Python_world\13.py", line 90, in divide
    result = x / y
             ~~^~~
TypeError: unsupported operand type(s) for /: 'str' and 'str'
"""

如上所示,任何情况下都会执行 finally 子句。except 子句不处理两个字符串相除触发的 TypeError,因此会在 finally 子句执行后被重新触发。

finally对于释放外部资源(文件、网络连接)都非常有用,无论是否成功使用资源,都会释放掉其连接

posted @ 2025-03-20 21:41  小鑫仔  阅读(31)  评论(0)    收藏  举报