Python——异常处理机制

Python 异常处理机制

Python异常与异常处理机制

Python使用称为异常的特殊对象来管理程序执行期间发生的错误(Python中一切皆对象,包括异常也是对象)。每当发生程序错误,都会创建一个异常对象。如果编写了处理该异常的代码,程序将继续运行;但是如果未编写代码对异常进行处理,程序将停止并显示traceback,其中包含有关异常的报告

异常处理是编程语言或计算机硬件里的一种机制,用于处理软件或信息系统中出现的异常状况,即超出程序正常执行流程的某些特殊条件。异常处理机制是使用 try-except 代码块处理异常,try-except 代码块让Python执行指定的操作,同时告诉Python发生异常时怎么办。使用try-except 代码块时,即便出现异常,程序也将继续运行,同时显示你编写的友好的错误消息,而不是令用户迷惑的traceback

Python提供了两个非常重要的功能来处理程序在运行中出现的异常和错误。经常使用的是try...except语句,拓展一下就是try-except-else-finally,另一个是断言,即assert

针对 Traceback 的解读

Traceback 是 Python 错误信息的报告,当你的程序导致异常时,Python 将打印 Traceback 以帮助你知道哪里出错了。虽然 Python 的 Traceback 提示信息看着挺复杂,但是里面存在丰富的信息,可以帮助你诊断和修复代码中引发异常的原因,以及定位到具体哪个文件的哪行代码出现的错误,这样有助于我们编写对应的异常处理代码来捕获异常,并给出合适的错误提示信息。所以说学会看懂 Traceback 信息是非常重要的

下面给出一个案例进行分析,这是一种导致Python引发异常的简单错误:除0

python 复制代码
# calculate.py文件的内容
def divide(a, b):	# 进行除法运算的函数
    return a / b

# main.py文件的内容
from calculate import divide

divide(5, 0)

# 抛出异常:
"""
Traceback (most recent call last):
  File "C:\编程\Python代码\test\main.py", line 3, in <module>
    divide(5, 0)
  File "C:\编程\Python代码\test\calculate.py", line 2, in divide
    return a / b
ZeroDivisionError: division by zero
"""
  • 错误输出的最后一行一般会告诉你引发了什么类型的异常,以及关于该异常的一些相关信息,所以看错误信息的最后一行就能获知到错误的原因
    • 在上述 traceback 中,最后一行指出的错误 ZeroDivisionError 就是一个异常对象,并且给出了具体的描述 division by zero,即表示因为除0,导致程序抛出该异常
  • 错误输出的前面几行的阅读顺序应该是由下而上的,因为越往上,离抛出异常的实际位置越远,越难定位到抛出异常的代码
  • 这一部分要每连续两行为一组进行阅读。其中每组的第1行会告诉你是在哪个文件的、哪个函数的、哪一行出错了,也就是会更直接的告诉你错误发生的位置;每组的第2行会把报错的那一行代码显示出来
  • 比如在程序里A函数调用了B函数,然后B函数调用了C函数,而C函数报错了,那么 traceback 中的前几行从下至上的顺序来看,会先显示C函数的信息,再显示B函数的信息,再显示A函数的信息,而且每一个函数的信息都是按两行来组织的,其中第1行展示位置,第2行展示代码
    • 在上述 traceback 中,前面几行中的最后一行定位的位置为位于 calculate.py 文件中的第2行,并且位于 divide 函数代码块中,具体抛出异常的代码为"return a / b"
    • 然后我们看上一行,定位的位置为位于 main.py 文件中的第3行,并且位于引入模块的代码中,具体抛出异常的代码为"divide(5, 0)"
  • 最上面第一行的内容是固定不变的,始终Traceback (most recent call last)",可以忽略

try-except-else-finally

  • try:正常情况下,程序计划执行的语句
  • except:程序异常时执行的语句
  • else:程序无异常即try段代码正常执行后会执行该语句
    • 当try语句的相关代码中的return,continue或break语句被执行时,else语句将被忽略
  • finally:不管有没有异常,都会执行的语句
    • 具体来说,当try,except或else语句的相关代码中存在某些跳转语句时,比如break,continue和return,与finally语句相关的代码将在这些跳转语句执行之前被执行
    • 在处理 Python 异常的过程中,一些代码需要始终被执行,无论是否有 Python 异常被抛出,或 Python 异常是否被处理。使用finally语句可以达成上述目标,该语句之后的代码通常与清理工作有关,比如,关闭打开的文件

运行逻辑1:首先运行try中的代码块,当出现异常时,终止try中代码块的执行,立即执行except中的代码块,最后执行finally中的代码块

python 复制代码
try:
    print('输出:我是try1')
    a = 5 / 0
    print('输出:我是try2')

except :
    print('输出:我是except')

else :
    print('输出:我是else')

finally :
    print('输出:我是finally')

# 输出结果:
"""
输出:我是try1
输出:我是except
输出:我是finally
"""

运行逻辑2:首先运行try中的代码块,当执行完毕后,代码始终没有抛出异常,继续执行else中的代码块,最后执行finally中的代码块

python 复制代码
try:
    print('输出:我是try1')
    a = 5 / 1
    print('输出:我是try2')

except :
    print('输出:我是except')

else :
    print('输出:我是else')

finally :
    print('输出:我是finally')

# 输出结果:
"""
输出:我是try1
输出:我是try2
输出:我是else
输出:我是finally
"""

运行逻辑3:函数中,如果finally语句的相关代码中包含了return语句,那么该return语句所返回的值(包括空值None),将取代try,except或else语句相关代码中的返回值

因为finally中的代码块在设定上必须执行:

  • 在try中的代码块return之前,会执行finally中的语句,try中的return被忽略了,最终返回的值是finally中return值
  • try中的代码块没有return语句,执行完毕后,会继续执行else中的语句,在else中的代码块return之前,执行finally中的语句,else中的return被忽略了,最终返回的值是finally中return值
  • try中的代码块中抛出异常,被except捕获,在except中的代码块return之前,执行finally中的语句,except中的return被忽略了,最终返回的值是finally中return值
python 复制代码
def test():
    try:
        print('输出:我是try1')
        a = 5 / 0
        print('输出:我是try2')
        return 1

    except :
        print('输出:我是except1')
        return 2
        print('输出:我是except2')

    else :
        print('输出:我是else')
        return 3

    finally :
        print('输出:finally')
        return 4

num = test()
print(num)

# 输出结果:
"""
输出:我是try1
输出:我是except1
输出:finally
4
"""


def test():
    try:
        print('输出:我是try1')
        a = 5 / 1
        print('输出:我是try2')
        return 1

    except :
        print('输出:我是except')
        return 2

    else :
        print('输出:我是else')
        return 3

    finally :
        print('输出:finally')
        return 4

num = test()
print(num)

# 输出结果:
"""
输出:我是try1
输出:我是try2
输出:finally
4
"""


def test():
    try:
        print('输出:我是try1')
        a = 5 / 1
        print('输出:我是try2')
        # return 1

    except :
        print('输出:我是except')
        return 2

    else :
        print('输出:我是else1')
        return 3
        print('输出:我是else2')

    finally :
        print('输出:finally')
        return 4

num = test()
print(num)

# 输出结果:
"""
输出:我是try1
输出:我是try2
输出:我是else1
输出:finally
4
"""

运行逻辑4:函数中,finally中不存在return,并且try中代码块没有抛出异常,那么按照 运行逻辑2,函数返回的是try或者else代码中先出现的那个return值

因为代码中程序正常运行,不一定要执行else中的代码块,如果try中return直接结束函数执行了,那么就不会执行else中的代码块

python 复制代码
def test():
    try:
        print('输出:我是try1')
        a = 5 / 1
        print('输出:我是try2')
        return 1

    except :
        print('输出:我是except')
        return 2

    else :
        print('输出:我是else')
        return 3

    finally :   # finally中不存在return语句
        print('输出:finally')
        # return 4  

num = test()
print(num)

# 输出结果:
"""
输出:我是try1
输出:我是try2
输出:finally
1
"""


def test():
    try:
        print('输出:我是try1')
        a = 5 / 1
        print('输出:我是try2')
        # return 1

    except :
        print('输出:我是except')
        return 2

    else :
        print('输出:我是else')
        return 3

    finally :   # finally中不存在return语句
        print('输出:finally')
        # return 4  

num = test()
print(num)

# 输出结果:
"""
输出:我是try1
输出:我是try2
输出:我是else
输出:finally
3
"""

运行逻辑5:函数中,finally中不存在return,并且try中代码块产生异常,那么按照 运行逻辑1,函数返回的是except代码中的那个return值

因为代码中程序抛出异常,必须执行except中的代码块

python 复制代码
def test():
    try:
        print('输出:我是try1')
        a = 5 / 0
        print('输出:我是try2')
        return 1

    except :
        print('输出:我是except')
        return 2

    else :
        print('输出:我是else')
        return 3

    finally :   # finally中不存在return语句
        print('输出:finally')
        # return 4  

num = test()
print(num)

# 输出结果:
"""
输出:我是try1
输出:我是except
输出:finally
2
"""

except语句

except语句的机制

每一个try语句,都必须至少有一个except语句,除非存在 finally 语句

  • 一般一个try语句后面都需要存在至少一个except语句,但是使用 finally 语句后 except 语句将成为可选的,try语句之后可以没有任何except语句
python 复制代码
try:
	print("No Error")
	
# 抛出异常:SyntaxError: unexpected EOF while parsing

try:
	5/0
	
# 抛出异常:SyntaxError: unexpected EOF while parsing


try:
	print("No Error")

finally:
	print("Something must be done")

# 输出结果:
"""
No Error
Something must be done
"""


try:
	5/0

finally:
	print("Something must be done")

# 输出结果:
"""
Something must be done
Traceback (most recent call last):
  File "C:\编程\Python代码\test\test.py", line 3, in <module>
    5/0
ZeroDivisionError: division by zero
"""

使用 else 语句的前提是至少拥有一个 except 语句

  • 如果要使用else语句来处理没有 Python 异常被引发的情况,那么在try语句之后,必须至少存在一个except语句
python 复制代码
try:
	print("No Error")

else:
	print("must have except")

finally:
	print("Something must be done")

# 抛出异常:SyntaxError: invalid syntax


try:
	print("No Error")

except:
	print("waiting for error")

else:
	print("must have except")

finally:
	print("Something must be done")

# 输出结果:
"""
No Error
must have except
Something must be done
"""

一个try...except语句中可以拥有多个except语句,都是最多只有一个 except 语句能与被抛出 Python 异常匹配

  • 如果try...except语句拥有多个except语句,那么与 Python 的 if 语句类似,他们会按照先后顺序进行匹配,当某一个except语句与被抛出的 Python 异常匹配时,其余的except语句将被忽略
python 复制代码
try:
	5/0

except IndexError as e:			# 无法捕获除0异常
	print("IndexError")

except ZeroDivisionError as e:	# 可以捕获除0异常
	print("ZeroDivisionError1")

except ZeroDivisionError as e:	# 可以捕获除0异常
	print("ZeroDivisionError2")

except BaseException as e:		# 可以捕获除0异常
	print("BaseException")

# 输出结果:ZeroDivisionError1


try:
	5/0

except IndexError as e:			# 无法捕获除0异常
	print("IndexError")

except BaseException as e:		# 可以捕获除0异常
	print("BaseException")

except ZeroDivisionError as e:	# 可以捕获除0异常
	print("ZeroDivisionError1")

except ZeroDivisionError as e:	# 可以捕获除0异常
	print("ZeroDivisionError2")

# 输出结果:BaseException

except可以处理一个专门的异常,也可以处理包含在元组中的一组异常

  • 可以将多个异常类型放在一个括号中,通过逗号分隔。当try块引发其中任何一个异常时,程序都将跳转到该except块,处理这个异常
python 复制代码
try:
	5 / 0

except (IndexError, ZeroDivisionError) as e:	# 可以捕获除0异常
	print("IndexError or ZeroDivisionError")

except BaseException as e:		# 可以捕获除0异常
	print("ZeroDivisionError1")

# 输出结果:IndexError or ZeroDivisionError

如果except后没有指定异常类型,则默认捕获所有的异常

  • 此时捕获异常范围为BaseException(后面会提到)
  • 这样的没有指定异常类型的 except 语句要求必须是最后一个 except 语句,否则程序报错
  • 原因很简单,如果它位于其他except语句之前,那么一些except语句将失去被执行的可能
python 复制代码
try:
	5 / 0

except:	# 可以捕获所有异常
	print("捕获到异常")

# 输出结果:捕获到异常

与所有 except 语句均不匹配的 Python 异常将被重新抛出

  • 如果一个 Python 异常被引发,并且与所有的except语句均不匹配,那么该异常将作为未处理的 Python 异常被重新抛出,这可能导致整个程序因此结束执行(当然,finally中的代码块依旧会被执行)
python 复制代码
try:
	5/0

except IndexError as e:		# 无法捕获除0异常
	print("IndexError")

except KeyError as e:		# 无法捕获除0异常
	print("KeyError")

except ValueError as e:		# 无法捕获除0异常
	print("ValueError")

finally:
	print('输出:finally')

# 抛出异常:
"""
输出:finally
Traceback (most recent call last):
  File "C:\编程\Python代码\test\main.py", line 2, in <module>
    5/0
ZeroDivisionError: division by zero
"""
在 except 语句中引用当前被处理的 Python 异常

在except语句中,使用as关键字可以指定一个与被处理异常绑定(指向异常对象)的标识符(可将其简单的视为 Python 变量),即可通过该标识符在except语句相关的代码中访问被处理的 Python 异常

此外,在 3.11 或更高版本中,通过sys模块的exception函数,同样可在except语句相关的代码中访问当前被处理的 Python 异常

  • 异常对象的常用属性
    • args:包含有关异常的错误编号和异常的描述的元组
    • strerror:异常的描述
    • errno:与异常的错误编号
python 复制代码
try:
	open("no_exit_file.txt", "r")

except FileNotFoundError as e:	# 可以捕获除0异常
    print("异常对象:", e)
    print("异常类型:", type(e))

    # 异常对象的常用属性
    print("异常信息: ", e.args)      # 该属性返回异常的错误编号和描述
    print("异常描述: ", e.strerror)  # 该属性返回异常的描述
    print("错误号: ", e.errno)       # 该属性返回异常的错误编号

# 输出结果:
"""
异常对象: [Errno 2] No such file or directory: 'no_exit_file.txt'
异常类型: <class 'FileNotFoundError'>
异常信息:  (2, 'No such file or directory')
异常描述:  No such file or directory
错误号:  2
"""

except 语句会删除使用 as 关键字与 Python 异常绑定的标识符

  • 如果在某个except语句中使用了as关键字,那么as关键字指定的标识符,将在该except语句的相关代码执行完毕时被删除。这意味着标识符仅在except语句中保持其正确性,他不应该与同一命名空间的其他标识符重复,以避免一些不必要的错误
python 复制代码
try:
	open("no_exit_file.txt", "r")

except FileNotFoundError as e:	# 可以捕获除0异常
    print("异常对象:", e)
    print("异常类型:", type(e))

print(e)

# 输出结果:
"""
异常对象: [Errno 2] No such file or directory: 'no_exit_file.txt'
异常类型: <class 'FileNotFoundError'>
Traceback (most recent call last):
  File "C:\编程\Python代码\test\test.py", line 9, in <module>
    print(e)
NameError: name 'e' is not defined
"""

finally语句

finally语句执行后才能抛出未被处理的异常

当try,except或else语句的相关代码引发不能被处理的 Python 异常时,这些异常不会被立即抛出,他们需要等待finally语句的相关代码的执行

python 复制代码
try:
	5/0

except IndexError as e:		# 无法捕获除0异常
	print("IndexError")

except KeyError as e:		# 无法捕获除0异常
	print("KeyError")

except ValueError as e:		# 无法捕获除0异常
	print("ValueError")

finally:
	print('输出:finally')

# 抛出异常:
"""
输出:finally
Traceback (most recent call last):
  File "C:\编程\Python代码\test\main.py", line 2, in <module>
    5/0
ZeroDivisionError: division by zero
"""


try:
    5 / 0

except ZeroDivisionError as e:
    print("ZeroDivisionError异常被捕获")
    raise e

finally:
    print('输出:finally')
# 输出结果:
"""
ZeroDivisionError异常被捕获
输出:finally
Traceback (most recent call last):
  File "C:\编程\Python代码\test\main.py", line 6, in <module>
    raise e
  File "C:\编程\Python代码\test\main.py", line 2, in <module>
    5 / 0
ZeroDivisionError: division by zero
"""


try:
    5 / 1

except ZeroDivisionError as e:
    print("ZeroDivisionError异常被捕获")

else:
    print('输出:else')
    raise ZeroDivisionError("重新抛出的ZeroDivisionError")

finally:
    print('输出:finally')
# 输出结果:
"""
输出:else
输出:finally
Traceback (most recent call last):
  File "C:\编程\Python代码\test\main.py", line 9, in <module>
    raise ZeroDivisionError("重新抛出的ZeroDivisionError")
ZeroDivisionError: 重新抛出的ZeroDivisionError
"""
finally中执行return会导致异常丢失

如果finally语句的相关代码中包含了跳转语句,比如break,continue或return,那么这些跳转语句的执行,将导致未被except处理的 Python 异常不再被重新抛出,即便这些异常是通过raise语句主动抛出的

3.8 版本之前,在 Python 的finally语句的相关代码中,不能使用continue语句

finally中出现return语句,如果try中抛出的异常没有被捕获到,按理说当finally执行完毕后,应该被再次抛出,但finally里执行了return,导致异常被丢失,所以实际应用中,不推荐在finally中使用return返回

python 复制代码
def test():
	try:
		5/0
		print("函数中的后续代码被执行")

	except IndexError as e:		# 无法捕获除0异常
		print("IndexError")

	except KeyError as e:		# 无法捕获除0异常
		print("KeyError")

	except ValueError as e:		# 无法捕获除0异常
		print("ValueError")

	finally:
		print('输出:finally')
		return 1

num = test()
print(num)

print("后续代码被执行")

# 输出结果:
"""
输出:finally
1
后续代码被执行
"""

raise 语句

raise 语句以及异常对象

使用 Python 提供的raise语句,开发人员可以主动抛出(引发)一个 Python 异常

python 复制代码
raise 语句基本的语法形式为 raise <exception>
  • exception
    • 被抛出的 Python 异常对象,或 Python 异常类型如果仅给出异常类型,那么将根据该类型隐式创建其对应的实例,比如,raise ZeroDivisionError的效果等同于raise ZeroDivisionError()
python 复制代码
raise ZeroDivisionError

# 抛出异常:
"""
Traceback (most recent call last):
  File "C:\编程\Python代码\test\main.py", line 1, in <module>
    raise ZeroDivisionError
ZeroDivisionError
"""

# 前后对比,发现效果一致
raise ZeroDivisionError()

# 抛出异常:
"""
Traceback (most recent call last):
  File "C:\编程\Python代码\test\main.py", line 1, in <module>
    raise ZeroDivisionError()
ZeroDivisionError
"""

创建异常对象时可以指定其魔法方法__str__的返回值

python 复制代码
# 判断ZeroDivisionError()返回的东西是否为ZeroDivisionError的实例对象
ins = ZeroDivisionError()
flag = isinstance(ins, ZeroDivisionError)	
print(flag)

# 输出结果:True

ins = ZeroDivisionError()
dir(ins)	# 可以获知,该实例对象具有魔法方法__str__

# 输出结果:
"""
['__cause__', '__class__', '__context__', '__delattr__', '__dict__', '__dir__', 
'__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__',
 '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', 
 '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', 
 '__subclasshook__', '__suppress_context__', '__traceback__', 'args', 'with_traceback']
"""

# 由于调用print函数实际是执行实例对象的魔法方法__str__,打印结果为该方法返回值
# 所以可以推断出实例化异常对象时,传入的"This is a ZeroDivisionError"
# 实际就是魔法方法__str__的返回值
ins = ZeroDivisionError("This is a ZeroDivisionError")
print(ins)	

# 输出结果:This is a ZeroDivisionError


ins = ZeroDivisionError("This is a ZeroDivisionError")
describe_string = ins.__str__()		# 手动调用魔法方法__str__,打印该方法的返回值
print(describe_string)

# 输出结果:This is a ZeroDivisionError

data = 10
ins = ZeroDivisionError("This is a ZeroDivisionError, carry a data: %s" % data )
print(ins)	

# 输出结果:This is a ZeroDivisionError, carry a data: 10

在except语句中,可以使用 raise 语句,将raise语句的exception部分留空,这会将当前被处理的 Python 异常重新抛出(引发)

但是以上做法的效果并不等同于调用exception函数的语句raise sys.exception(),或类似于raise err的语句(假设err为as关键字绑定的标识符),他们会展示不同的回溯(Traceback)信息

python 复制代码
# calculate.py文件的内容
def divide(a, b):	# 进行除法运算的函数
    return a / b

# main.py文件的内容
from calculate import divide

divide(5, 0)

# 抛出异常:
"""
Traceback (most recent call last):
  File "C:\编程\Python代码\test\main.py", line 3, in <module>
    divide(5, 0)
  File "C:\编程\Python代码\test\calculate.py", line 2, in divide
    return a / b
ZeroDivisionError: division by zero
"""


# calculate.py文件的内容
def divide(a, b):	# 进行除法运算的函数
    return a / b

# main.py文件的内容
from calculate import divide

try:
    divide(5, 0)

except ZeroDivisionError:
    print("ZeroDivisionError异常被捕获")
    raise	# 使用raise重新抛出异常
    
# 抛出异常:
"""
ZeroDivisionError异常被捕获
Traceback (most recent call last):
  File "C:\编程\Python代码\test\test.py", line 4, in <module>
    divide(5, 0)
  File "C:\编程\Python代码\test\calculate.py", line 2, in divide
    return a / b
ZeroDivisionError: division by zero
"""


# calculate.py文件的内容
def divide(a, b):	# 进行除法运算的函数
    return a / b

# main.py文件的内容
from calculate import divide

try:
    divide(5, 0)

except ZeroDivisionError as e:
    print("ZeroDivisionError异常被捕获")
    raise e	
   
# 抛出异常:
"""
ZeroDivisionError异常被捕获
Traceback (most recent call last):
  File "C:\编程\Python代码\test\test.py", line 8, in <module>
    raise e
  File "C:\编程\Python代码\test\test.py", line 4, in <module>
    divide(5, 0)
  File "C:\编程\Python代码\test\calculate.py", line 2, in divide
    return a / b
ZeroDivisionError: division by zero
"""

抛出异常时,自动创建的异常对象的魔法方法__str__的返回值即为 traceback 最后一行针对该异常的描述信息

python 复制代码
5 / 0

# 抛出异常:
"""
Traceback (most recent call last):
  File "C:\编程\Python代码\test\main.py", line 1, in <module>
    5 / 0
ZeroDivisionError: division by zero
"""


try:
    5 / 0

except ZeroDivisionError as e:
	# 判断 e 是否为ZeroDivisionError的实例对象
    flag = isinstance(e, ZeroDivisionError)
    print(flag)
	
	# 手动调用魔法方法__str__,打印该方法的返回值,进行对比
    describe_string = e.__str__()	
    print(e)
    print(describe_string)

# 输出结果:
"""
True
division by zero
division by zero
"""


try:
    5 / 0

except ZeroDivisionError as e:
    print("自动抛出的异常被捕获")
    raise e

# 输出结果:
"""
自动抛出的异常被捕获
Traceback (most recent call last):
  File "C:\编程\Python代码\test\main.py", line 6, in <module>
    raise e
  File "C:\编程\Python代码\test\main.py", line 2, in <module>
    5 / 0
ZeroDivisionError: division by zero
"""

自行创建一个异常对象,通过raise抛出,同时携带自定义的描述信息

python 复制代码
try:
    5 / 0

except ZeroDivisionError as e:
    print("自动抛出的异常被捕获")
    raise ZeroDivisionError("<division by zero>")

# 输出结果:
"""
自动抛出的异常被捕获
Traceback (most recent call last):
  File "C:\编程\Python代码\test\main.py", line 6, in <module>
    raise ZeroDivisionError("<division by zero>")
ZeroDivisionError: <division by zero>
"""

raise...from 语句

如果一个 Python 异常已经被某个except语句处理,而该except语句的相关代码引发了新的异常,如果新的 Python 异常是通过raise...from语句引发,那么可以为新的 Python 异常指定一个表示原因的异常,用于说明新的 Python 异常是由该异常导致的

raise...from语句可以在其他位置使用,如果位于except语句的相关代码中,那么表示原因的异常一般被指定为已被except处理的 Python 异常,此时回溯信息将优先展示表示原因的 Python 异常的信息

python 复制代码
raise...from 语句基本的语法形式为 raise <newexception> from <causeexception>
  • newexception

    • 被抛出的 Python 异常对象,或 Python 异常类型。如果仅给出异常类型,那么将根据该类型隐式创建其对应的实例,比如,raise RuntimeError from ValueError()的效果等同于raise RuntimeError() from ValueError()
  • causeexception

    • 表示原因的 Python 异常对象,或 Python 异常类型。如果仅给出异常类型,那么将根据该类型隐式创建其对应的实例,比如,raise RuntimeError() from ValueError的效果等同于raise RuntimeError() from ValueError()

Python 异常的基类 BaseException

在 Python 中,所有异常(表示异常的类)都需要继承自BaseException或Exception,这包括 Python 的内置异常,以及由开发人员定义的异常(当然,只有少数 Python 异常直接继承自类BaseException,大部分 Python 异常均继承自类Exception或类Exception的派生类)

  • Python 异常基类 BaseException 和 Exception 之间的区别
    • Exception是BaseException类的派生类,他表示不是来自于系统的非正常情况,比如,表示除数为0的 Python 异常ZeroDivisionError
    • 一般情况下,开发人员仅需要捕获从Exception类派生的各种 Python 异常,如果将捕获的范围扩大到BaseException,那么可能会导致一些意想不到的问题
    • 如果except后没有指定异常类型,则默认捕获所有的异常,即此时捕获异常范围为BaseException, 这样的没有指定异常类型的 except 语句要求必须是最后一个 except 语句,否则程序报错,原因很简单,如果它位于其他except语句之前,那么一些except语句将失去被执行的可能

如果将捕获异常的范围扩大到BaseException,可能会导致sys.exit()无法退出 Python 解释器

python 复制代码
import sys

try:
	sys.exit()	# 尝试退出程序
except BaseException as e:
	print(f'捕获到了异常 {type(e)}')

print('sys.exit() 已经执行,但是程序没有成功退出')

# 输出结果:
"""
捕获到了异常 <class 'SystemExit'>
sys.exit() 已经执行,但是程序没有成功退出
"""
相关推荐
老白干36 分钟前
jjwt 0.9.1 在 JDK 11+ 上的两个“坑”与完整解决方案
java·python·log4j
笨鸟先飞,勤能补拙7 小时前
AI 赋能网络安全:技术全景、成熟度评估与实战案例
人工智能·python·安全·web安全·网络安全·sqlite·github
长和信泰光伏储能8 小时前
京津冀光伏发电:绿色能源的未来之路
python·能源
浦信仿真大讲堂8 小时前
从重复操作到自动化闭环:如何让 CST 与 Python 真正协同起来
python·自动化·cst·仿真软件·达索软件
Gu Gu Study9 小时前
ScoutLoop开放域深度研究引擎(agent的初步设计想法)
人工智能·python
卷无止境10 小时前
写代码这件事,到底该讲究点什么?
后端·python
卷无止境10 小时前
循环复杂度到底在算什么,Python 代码怎么才能写得让人一看就懂
后端·python
lpfasd12310 小时前
MediaCrawler 项目深度分析
chrome·python·chrome devtools
Dxy123931021610 小时前
Python项目打包成EXE完整教程(PyInstaller实战避坑)
开发语言·python
bamb0011 小时前
一个项目带你入门AI应用开发01
python