Python异常

1.异常是什么

Python使用异常对象来表示异常状态,并在遇到错误时引发异常。异常对象未被处理或捕获时,程序将终止并显示一条错误消息(traceback)。每个异常都是某个类的实例。可以以各种方式引发和捕获这些实例,从而逮住错误并采取措施,而不是放任整个程序的失败。

2.raise语句

如果要引发异常可以使用raise语句,并将一个类或实例作为参数。将类作为参数时,将自动创建一个实例。

>>>raise Exception
Traceback (most recent call last):
  File "<pyshell#0>", line 1, in <module>
    raise Exception
Exception
#这里引发的是通用异常,没有指出出现了什么错误
>>>raise Exception('hyperdrive overload')
Traceback (most recent call last):
  File "<pyshell#1>", line 1, in <module>
    raise Exception('hyperdrive overload')
Exception: hyperdrive overload
#和第一个示例相比,多了错误信息hyperdrive overload

3.一些内置的异常类

在这里插入图片描述

4.自定义的异常类

虽然内置的异常类涉及的范围非常广,能够满足很多需求,但在有些情况下只能自己创建异常类,才能满足需求。
如何创建异常类呢?就像创建其他类一样,但务必要直接或间接地继承Exception(这意味着从任何内置异常类派生都可以)
自定义异常类的代码类似于下面这样:

class SomeCustomException(Exception):pass

5.捕获异常

异常比较有趣的地方是可以对其进行处理,通常称之为捕获异常。为此,可以使用try/except语句。

x=int(input('第一个数:'))
y=int(input('第二个数:'))
print(x/y)
#运行结果
第一个数:2
第二个数:4
0.5
----------
第一个数:100
第二个数:0
Traceback (most recent call last):
  File "/Users/liqiming/PycharmProjects/pythonProject/venv/代码/7.py", line 3, in <module>
    print(x/y)
ZeroDivisionError: division by zero

我们可以看到程序出现了异常。为捕获这种异常并对错误进行处理(即输出对用户更友好的信息),可以像下面重写这个程序:

try:
    x = int(input('第一个数:'))
    y = int(input('第二个数:'))
    print(x / y)
except ZeroDivisionError:
    print('第二个数不能为0⃣️')
#运行结果
第一个数:2
第二个数:0
第二个数不能为0⃣️#文本好像不支持0⃣️

使用一条if语句似乎更简单些,但如果有多个运算,那每个运算都需要一条if语句;而使用try/except语句,只需要一个错误处理程序。

1).重新引发

捕获异常后,想要重新引发它;可以使用raise语句且不提供任何参数(或者显示提供捕获的异常)

>>>class MuffledCalculator:
       muffled=False
       def calc(self,expr):
           try:
               return eval(expr)
           except ZeroDivisionError:
               if self.muffled:
                   print('Division by zero is illegal')
               else:
                   raise
...
>>>calculator=MuffledCalculator()
>>>calculator.calc('10/10')
1.0
>>>calculator.calc('10/0')
Traceback (most recent call last):#报错
  File "<pyshell#14>", line 1, in <module>
    calculator.calc('10/0')
  File "<pyshell#11>", line 5, in calc
    return eval(expr)
  File "<string>", line 1, in <module>
ZeroDivisionError: division by zero
>>>calculator.muffled=True#关闭了抑制功能
>>>calculator.calc('10/0')#如你所见,关闭抑制功能后,捕获了异常ZeroDivisionError,但继续向上传播它
Division by zero is illegal

⚠️⚠️⚠️
发生去零行为时,如果启用了“抑制”功能,方法calc将隐式地返回None。换而言之,若启用抑制功能,就不应依赖返回值。


当无法处理异常时,在except子句中使用不带参数的raise是不错的选择;如果想要引发其他异常,在这种情况下,会导致进入except子句的异常将被作为异常上下文存储起来,并出现在最终的错误信息中。

>>>try:
...    1/0
...except ZeroDivisionError:
...    raise valueError
...
Traceback (most recent call last):
  File "<pyshell#10>", line 2, in <module>
    1/0
ZeroDivisionError: division by zero

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<pyshell#10>", line 4, in <module>
    raise valueError

你可以使用raise…from…语句来提供自己的异常上下文,也可使用None来禁用上下文。

>>>try:
...    1/0
...except ZeroDivisionError:
...   raise ValueError from None
...
Traceback (most recent call last):
  File "<pyshell#4>", line 4, in <module>
    raise ValueError from None
ValueError
2).多个except子句
try:
    x = int(input('第一个数:'))
    y = int(input('第二个数:'))
    print(x / y)
except ZeroDivisionError:
    print('第二个数不能为0⃣️')
#运行结果
第一个数:2
第二个数:"Hi"

如果你输入这样的参数。那将会引发另一种错误。由于程序中的except子句只捕获ZeroDivisionError异常,这种异常将引发程序终止。如果要解决这个问题就要再添加一个except子句。

except TypeError:
    print('它不是一个数字!')

现在使用if语句就有些困难了。大量的if语句会使程序的可读性变差。

4).捕获多个异常

如果要使用一个except子句,可使用一个元组指定这些异常。

except (ZeroDivisionError,TypeError,NameError)
   print('输入错误')
5)捕获对象

要在except子句访问这些异常对象本身,可使用两个参数(注意不是一个参数)
在需要让对象继续运行并记录错误时很有用。

try:
    x = int(input('第一个数:'))
    y = int(input('第二个数:'))
    print(x / y)
except (ZeroDivisionError,TypeError) as:
    print()

在这个程序中,except子句也捕获了两种异常,同时也捕获了对象本身,因此可以将其打印出来,让用户知道发生了什么情况。

6).捕获所有异常

即使程序处理了好几种异常,仍然有可能有漏网之鱼。例如:在执行除法运算的程序时,用户在提示时不输入任何内容就回车,将出现一条错误信息,还有一些相关问题出现在什么地方的信息(栈跟棕
如果你想要使用一段代码就捕获所有的异常,只需在except子句中不指定任何异常类即可。

try:
    x = int(input('第一个数:'))
    y = int(input('第二个数:'))
    print(x / y)
exceptprint('输入不正确')

但是,像这样捕获所有的异常很危险,因为这不仅会隐藏你已知的错误,还会隐藏你没有考虑到的错误。这还将捕获用户使用Ctrl+C终止执行的企图、调用函数sys.exit来终止执行的企图等等。大多数情况下,更好的选择是使用except Exception as e并对异常对像进行检查。这样做的话会让不是从Exception派生而来的异常成为漏网之鱼,其中包括SystemExit和KeyboardInterrupt,因为它们是从BaseException派生而来。

7).加else子句

有些时候,在没有出现异常时运行一个代码块很有用。为此,可以在try/except语句后添加一个else子句。

while True:
    try:
        x = int(input('第一个数:'))
        y = int(input('第二个数:'))
        value=x/y
        print('x / y 是',value)
    except:
        print('请重新输入!')
    else:
        break
#运行结果
第一个数:1
第二个数:0
请重新输入!
第一个数:2
第二个数:age
请重新输入!
第一个数:2
第二个数:3
x / y 是 0.6666666666666666

在这里,只有没有异常时才会终止程序。

8).finally子句

它可用于在发生异常时执行清理工作。和try子句配套。

X=None
try:
    x=10/0
finally:
    print(66)
    del X

#运行结果
66
Traceback (most recent call last):
  File "/Users/liqiming/PycharmProjects/pythonProject/venv/代码/10.py", line 3, in <module>
    x=10/0
ZeroDivisionError: division by zero

在上述示例中,不管try子句发生什么异常,都将执行finally子句。为何在try子句之前初始化x呢?因为如果不这样做,ZeroDivisionError将导致根本没有机会给它赋值,进而导致finally子句中对其执行del时引发未捕获的异常。程序在执行清理工作后崩溃。
finally子句非常适合用于确保文件或网络套接字等得以关闭。
也可在一条语句中同时包含try、except、finally、else。(或其中的三个)

try:
	1/0
except NameError:
	print('未知数')
else:
	print('一切顺利')
finally:
	print('清理')

6.函数与异常

如果不处理函数中引发的异常,它将向上传播到调用函数的地方,如果在哪里也没有得到处理;异常将继续向上传播,直至达到主程序(全局作用域)。如果主程序中也没有异常处理程序,程序将终止并显示栈跟踪信息。

>>>def faulty():
...		raise Exception('Something is wrong')
...
>>>def ignore_exception():
...		fautly()
...
>>>def handle_exception():
...		try:
...			fautly()
...		except:
...			print('Exception handled')
...
>>>ignore_exception()
Traceback (most recent call last):
  File '<stdin>',line1,in ?
  File '<stdin>',line2,in ignore_exception
  File '<stdin>',line2,in fautly
Exception:Something is wrong
>>>handle_exception
Exception handled

如你所见,faulty引发的异常依次向外传播,最终导致显示一条栈跟棕消息。调用handle_exception时,异常最终传播到handle_exception,并被这里的try/except语句处理。

7.不显示栈跟踪信息

你知道代码可能引发某种异常,又不希望出现这种异常时程序终止并显示栈跟踪信息。你可以添加必要的try/except或try/finally或结合使用来处理它。
有时候可以使用条件语句来达成异常处理实现的目标,但那样编写出来的代码可能不那么自然,可读性不那么高。

def describe_person(oerson):
	print('Desciption of',person['name'])
	print('Age:',person['age'])
	if 'occupation' in person:
		print('Occupation:',person['occupation'])
#调用这个函数,并向他提供提供包含姓名黎明和年龄20的字典。
Desciption of:黎明
Age:20
#如果你加入职业信息——歌手
Desciption of:黎明
Age:20
Occupation:歌手

这段代码很直观,但效率不高(虽然代码简洁),因为它必须两次查找‘occupation’键:一次检查键是否存在,一次获取这个键关联的值,以便将其答印出来。下面是另一种解决方案:

def describe_person(person):
	print('Desciption of',person['name'])
	print('Age:',person['age'])
	try:
		print('Occupation:',person['occupation'])
	except KeyError:pass

在这里,函数直接假设存在’occupation’键。若果假设正确,就直接获取并打印值。而无需检查这个键是否存在。如果不存在,将引发KeyError异常,而except子句将捕获这个异常。


在检查对象是否包含特定的值时,try/except也很有用。

try:
	obj.write
except AttributeError:
	print('The object is not writeable')
else:
	print('The object is writeable')

在这里,try子句只访问属性write,而没有使用它来做任何事情。如果引发了AttributeError异常,说明对象没有属性write,否则就有这个属性。

8.发出警告

使用模块warnings中的函数warn。
警告只显示一次。再次运行,什么都不会发生。
如果其他代码在使用你的模块,可使用模块warnings中的函数filterwarnings来抑制你发出的警告或特定类型的警告,并采取指定的措施,如:‘error’或‘ignore’

>>>from warnings import warn
>>>warn("I've got a bad feeling about this.")
_main_:1:UseWarning:I've got a bad feeling about this.
>>>
>>>from warnings import filterwarnings
>>>filterwarnings('ignore')
>>>warn('Anyone out there?')
>>>filterwarnings('error')
>>>warn('Something is very wrong!')
Traceback(most recent call last):
   File"<stdin>",line 1, in <module>
UserWarning:Something is very wrong!

如你所见,引发的异常为UserWarning。发出警告时,可指定将引发的异常(警告类型),但必须是Warning的子类。若将警告转化为错误,将使用你指定的异常。还可根据异常来过滤特定类型的警告。

>>>filterwarnings('error')
>>>warn("This function is really old...", DeprecationWarning)
Traceback (most recent call last):
	File "<stdin>", line 1,in <module>
DeprecationWarning: This function is really old...
>>>filterwarnings("ignore",category=DeprecationWarning)
>>>warn("Another deprecation warning.", DeprecationWarning)
>>> warn("Something else.")
Traceback (most recent call last):
	File "<stdin>", line 1, in <module>
UserWarning: Something else.
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值