异常是程序运行时可能发生的错误或意外情况。在Python中,异常是一种对象,表示程序执行期间发生的错误。
当出现异常时,程序的正常流程会被中断,而是跳转到异常处理流程。
异常的分类
- 内建异常
Built-in Exceptions
:由python内部定义的异常,例如ValueError
KeyError
等。- 用户自定义的异常:就是程序员自己去定义的异常,根据实际应用场景去使用。
常见的异常类型如下:
异常名称 | 类型描述 |
---|---|
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
关键字:
-
引发内置异常:
raise ValueError("Invalid value") # 抛出 ValueError 异常,并指定错误消息
-
引发自定义异常:
class MyCustomException(Exception): pass raise MyCustomException("This is a custom exception") # 抛出自定义异常,并指定错误消息
-
捕获异常并重新引发:
try: # 一些可能引发异常的代码 raise ValueError("Invalid value") except ValueError as e: print("Caught an exception:", e) raise # 重新引发异常
-
异常链:
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语句是有副作用的,因此不应该在实现的逻辑中使用。