异常处理

异常是程序运行时可能发生的错误或意外情况。在Python中,异常是一种对象,表示程序执行期间发生的错误。

当出现异常时,程序的正常流程会被中断,而是跳转到异常处理流程。

异常的分类

  1. 内建异常Built-in Exceptions:由python内部定义的异常,例如ValueError KeyError等。
  2. 用户自定义的异常:就是程序员自己去定义的异常,根据实际应用场景去使用。

常见的异常类型如下:

异常名称 类型描述
SystemExit 解释器请求退出
KeyboardInterrupt 用户中断执行(通常是输入^C)
Exception 常规错误的基类
StopIteration 迭代器没有更多的值
GeneratorExit 生成器(generator)发生异常来通知退出
StandardError 所有的内建标准异常的基类
ArithmeticError 所有数值计算错误的基类
FloatingPointError 浮点计算错误
OverflowError 数值运算超出最大限制
ZeroDivisionError 除(或取模)零 (所有数据类型)
AssertionError 断言语句失败
AttributeError 对象没有这个属性
EOFError 没有内建输入,到达EOF 标记
EnvironmentError 操作系统错误的基类
IOError 输入/输出操作失败
OSError 操作系统错误
WindowsError 系统调用失败
ImportError 导入模块/对象失败
LookupError 无效数据查询的基类
IndexError 序列中没有此索引(index)
KeyError 映射中没有这个键
MemoryError 内存溢出错误(对于Python 解释器不是致命的)
NameError 未声明/初始化对象 (没有属性)
UnboundLocalError 访问未初始化的本地变量
ReferenceError 弱引用(Weak reference)试图访问已经垃圾回收了的对象
RuntimeError 一般的运行时错误
NotImplementedError 尚未实现的方法
SyntaxError Python 语法错误
IndentationError 缩进错误
TabError Tab 和空格混用
SystemError 一般的解释器系统错误
TypeError 对类型无效的操作
ValueError 传入无效的参数
UnicodeError Unicode 相关的错误
UnicodeDecodeError Unicode 解码时的错误
UnicodeEncodeError Unicode 编码时错误
UnicodeTranslateError Unicode 转换时错误

Warning(警告的基类)

异常名称 类型描述
DeprecationWarning 关于被弃用的特征的警告
FutureWarning 关于构造将来语义会有改变的警告
OverflowWarning 旧的关于自动提升为长整型(long)的警告
PendingDeprecationWarning 关于特性将会被废弃的警告
RuntimeWarning 可疑的运行时行为(runtime behavior)的警告
SyntaxWarning 可疑的语法的警告
UserWarning 用户代码生成的警告

主动触发异常

raise 是 Python 中用于触发异常的关键字。通过使用 raise 关键字,您可以在代码中主动引发异常,从而实现错误处理和异常传递的机制。

主动触发异常之后,raise之后的代码就不会被运行了

以下是 raise 的基本语法:

raise [ExceptionType[(args)]]
  • ExceptionType 是异常的类型,可以是内置的异常类,也可以是自定义的异常类。
  • (args) 是可选的,用于传递给异常类的参数。

下面是一些示例,展示了如何使用 raise 关键字:

  1. 引发内置异常:

    raise ValueError("Invalid value")   # 抛出 ValueError 异常,并指定错误消息
    
  2. 引发自定义异常:

    class MyCustomException(Exception):
        pass
    
    raise MyCustomException("This is a custom exception")   # 抛出自定义异常,并指定错误消息
    
  3. 捕获异常并重新引发:

    try:
        # 一些可能引发异常的代码
        raise ValueError("Invalid value")
    except ValueError as e:
        print("Caught an exception:", e)
        raise   # 重新引发异常
    
  4. 异常链:

    try:
        # 一些可能引发异常的代码
        raise ValueError("Invalid value")
    except ValueError as e:
        raise TypeError("Type error occurred") from e   # 引发新的异常,并将原始异常设置为其上下文
    

使用 raise 关键字可以帮助您在适当的时候引发异常,并进行错误处理。这样可以使您的代码更加健壮和可靠。


def functionName( level ):
    if level < 1:
        raise Exception("Invalid level!", level)
        # 触发异常后,后面的代码就不会再执行
while True:
    num = 0
    if 5 / num:
        raise ZeroDivisionError("除数不能为0")
ZeroDivisionError: division by zero
number = input("输入一个整数:")
if not number.isdigit():
    raise ValueError("只能输入整数!")
ValueError: 只能输入整数!

异常处理

捕获异常可以使用try/except语句

try块

try 块用于包含可能会引发异常的代码。在 try 块中,您可以放置那些可能会出错的代码。

如果 try 块中的代码引发了指定的异常,程序会跳转到相应的 except 块来处理异常。如果没有指定异常类型,except 块将捕获所有异常。

try:
    # 可能引发异常的代码
except ExceptionType:  # 指定要捕获的异常类型
    # 异常处理代码

except块

except 块用于处理 try 块中引发的异常。在 except 块中,您可以编写处理异常的代码逻辑。

try:
    # 可能引发异常的代码
except ExceptionType as e:  # 指定要捕获的异常类型,并将异常对象赋值给变量 e
    # 异常处理代码
try:
    name = "小满"
    name[1] = "乔"
except TypeError as e:
    print("触发了异常:", e)  # 触发了异常: 'str' object does not support item assignment

在 except 块中,您可以使用异常对象 e 来访问有关异常的信息,并编写相应的处理逻辑。

except块中,如果包含多个异常,可以使用一个元组接收。

try:
    int("a")
    nameList = ["小乔", "大乔", "小满"]
    nameList[5]
except (ValueError, IndexError) as e:
    print("触发了异常:", e)
触发了异常: invalid literal for int() with base 10: 'a'
try:
    # int("a")
    nameList = ["小乔", "大乔", "小满"]
    nameList[5]
except (ValueError, IndexError) as e:
    print("触发了异常:", e)
触发了异常: list index out of range

不带任何异常类型使用except

try:
	触发异常的语句
except:
	pass
try:
    int("a")
except:
    pass

# 结果不会报错,也没有任何输入

在循环中的灵活使用

for index in range(-2, 3):
    try:
        print(f"5/{index}={5/index}")
    except Exception as e:
        print(f"发生了异常{e},已跳过。。")
    continue
5/-2=-2.5
5/-1=-5.0
发生了异常division by zero,已跳过。。
5/1=5.0
5/2=2.5
num = 2
while num > -3:
    num -= 1
    try:
        print(f"5/{num}={5/num}")
    except Exception as e:
        print(f"触发异常[{e}]已自动跳过。")
        continue
5/1=5.0
触发异常[division by zero]已自动跳过。
5/-1=-5.0
5/-2=-2.5
5/-3=-1.6666666666666667
num = 4
while num > -3:
    num -= 1
    try:
        print(f"5/{num}={5/num}")
    except Exception as e:
        print(f"触发异常[{e}],程序已自动结束。")
        break
5/3=1.6666666666666667
5/2=2.5
5/1=5.0
触发异常[division by zero],程序已自动结束。

在实际代码中不建议使用不带任何异常类型的except语句,因为它会捕获所有异常,包括程序中可能不期望捕获的异常,使得排查问题变得困难。

else块

else 块是可选的,它用于在 try 块中没有引发异常时执行的代码。如果在 try 块中没有引发异常,程序将跳过 except 块并执行 else 块中的代码。

try:
    # 可能引发异常的代码
except ExceptionType:
    # 异常处理代码
else:
    # 没有异常时执行的代码

finally块

finally 块是可选的,它用于包含无论是否引发异常都将执行的代码。无论 try 块中是否引发异常,finally 块中的代码都会被执行。

try:
    # 可能引发异常的代码
except ExceptionType:
    # 异常处理代码
finally:
    # 总是会执行的代码

finally 块通常用于释放资源或进行清理操作,无论是否发生异常,都可以确保执行这些必要的代码。

try:
    int("a")
except Exception as e:
    print(f"发生了[{e}]异常。")
else:
    print("没有任何异常发生,else语句被执行了")
finally:
    print("程序已结束")
发生了[invalid literal for int() with base 10: 'a']异常。
程序已结束
try:
    print("小满最棒了!")
except Exception as e:
    print(f"发生了[{e}]异常。")
else:
    print("没有任何异常发生,else语句被执行了")
finally:
    print("程序已结束")
小满最棒了!
没有任何异常发生,else语句被执行了
程序已结束

总结

try:
    # 可能引发异常的代码
except ExceptionType:
    # 异常处理代码
else:
    # 没有异常时执行的代码
finally:
    # 有没有异常,都会被执行

在实际开发过程中,异常处理的代码通常放到else去执行

在实际开发中,将异常处理的代码放在 else 块中是一种常见的做法。这样可以使代码更清晰和易读。

else 块中的代码只会在 try 块中没有引发异常时执行。这意味着如果没有异常发生,我们可以在 else 块中执行一些特定的逻辑。

以下是一个示例,展示了将异常处理代码放在 else 块中的情况:

try:
    # 可能引发异常的代码
except ExceptionType:
    # 异常处理代码
else:
    # 没有异常时执行的代码

在这个示例中,如果 try 块中的代码没有引发指定的异常,程序将跳过 except 块并执行 else 块中的代码。

将异常处理代码放在 else 块中有几个优点:

  • 代码逻辑更清晰:将异常处理的代码和正常执行的代码分开,使得代码更易读和理解。
  • 避免不必要的捕获:只有在异常发生时才会执行 except 块,而在 else 块中的代码将不会执行。这样可以避免捕获不必要的异常。
  • 提前发现错误:如果 else 块中的代码没有执行,说明在 try 块中发生了异常,这有助于及早发现和调试问题。
def divide_numbers(a, b):
    try:
        result = a / b
    except ZeroDivisionError:
        print("Error: Cannot divide by zero!")
    else:
        print(f"The result of division is: {result}")

# 调用函数并测试异常处理
divide_numbers(10, 2)  # 输出: The result of division is: 5.0

divide_numbers(10, 0)  # 输出: Error: Cannot divide by zero!

当然,在具体的开发场景中,您可以根据需要选择适合的方式来组织代码。有时将异常处理代码放在 except 块中也是合理的,这取决于具体的业务逻辑和代码结构。


断言

在Python中,断言(assertion)是一种用于验证代码假设的机制。它用于在代码中插入一些检查点,以确保某个条件为真。如果断言的条件为假,那么会引发 AssertionError 异常。

断言的语法如下:

assert condition, message

其中,condition 是需要进行检查的条件,如果条件为假,则会引发 AssertionError 异常。message 是可选的,用于在异常中提供更具体的错误信息。

以下是一些示例,展示了如何使用断言:

def divide_numbers(a, b):
    assert b != 0, "Error: Cannot divide by zero!"
    result = a / b
    return result

# 调用函数并测试断言
print(divide_numbers(10, 2))  # 输出: 5.0

print(divide_numbers(10, 0))  # 引发 AssertionError,输出: AssertionError: Error: Cannot divide by zero!

在这个示例中,divide_numbers 函数使用断言来确保除数 b 不为零。如果 b 的值为零,断言条件为假,会引发 AssertionError 异常,并显示指定的错误信息。

断言在开发和调试过程中非常有用,可以帮助我们捕获潜在的错误和问题。然而,它们通常在测试和调试阶段使用,并不适合用于处理预期的错误情况。

请注意,在生产环境中,为了安全起见,通常会禁用断言。这可以通过使用 -O-OO 命令行参数来实现。

断言可以和try/except一起使用

在某些情况下,我们可能希望在代码中使用断言来确保某个条件为真,并在条件为假时引发异常。同时,使用 try-except 可以捕获这个异常并进行适当的处理。

下面是一个示例,展示了如何结合使用断言和 try-except

def divide_numbers(a, b):
    try:
        assert b != 0, "Error: Cannot divide by zero!"
        result = a / b
        return result
    except AssertionError as e:
        print(e)
        # 进行其他异常处理操作

# 调用函数并测试断言和异常处理
print(divide_numbers(10, 2))  # 输出: 5.0

print(divide_numbers(10, 0))  # 输出: Error: Cannot divide by zero!

在这个示例中,我们在 divide_numbers 函数中使用断言来确保除数 b 不为零。如果条件为假,断言会引发 AssertionError 异常。然后,我们使用 try-except 结构来捕获这个异常,并进行适当的处理。

请注意,使用断言时需要谨慎。断言应该用于检查代码中的不变量和假设,而不是用于处理预期的错误情况。因此,结合 try-except 来捕获断言失败的异常是一个较好的做法,以便在开发和调试过程中能够及时发现问题。

for index in range(-2, 3):
    try:
        assert index != 0, "0不能作为除数"
        result = 5 / index
    except Exception as e:
        print(f"触发了异常[{e}],已结束。")
        break
    else:
        print(result)
-2.5
-5.0
触发了异常[0不能作为除数],已结束。

注意点

  • 需要注意的是,assert语句通常用于调试和测试时使用。
  • 在生产环境中,它可能会导致意想不到的错误产生,因此不应常规使用。
  • 并且,assert语句是有副作用的,因此不应该在实现的逻辑中使用。
posted @ 2023-12-06 14:50  小满三岁啦  阅读(8)  评论(0编辑  收藏  举报