函 数 | 描 述 |
---|---|
warnings.filterwarnings(action,category=Warning, ...) |
用于过滤警告 |
warnings.warn(message, category=None) |
用于发出警告 |
def faulty():
raise Exception('Something is wrong')
def ignore_exception():
faulty()
def handle_exception():
try:
faulty()
except:
print('Exception handled')
# ignore_exception() #此代码会报错
handle_exception()
Exception handled
faulty
中引发的异常依次从 faulty
和 ignore_exception
向外传播,
最终导致显示一条栈跟踪消息。调用 handle_exception
时,
异常最终传播到 handle_exception
,并被这里的 try/except
语句处理。
异常处理并不是很复杂。如果知道代码可能引发某种异常,且不希望出现这种异常时程序 终止并显示栈跟踪消息,
可添加必要的 try/except
或 try/finally
语句(或结合使用)来处理它。
有时候,可使用条件语句来达成异常处理实现的目标,但这样编写出来的代码可能不那么自 然,可读性也没那么高。
另一方面,有些任务使用 if/else
完成时看似很自然,但实际上使用 try/except
来完成要好得多。
下面来看两个示例:
假设有一个字典,要在指定的键存在时打印与之相关联的值, 否则什么都不做。实现这种功能的代码可能类似于下面这样:
def describe_person(person):
print('Description of', person['name'])
print('Age:', person['age'])
if 'occupation' in person:
print('Occupation:', person['occupation'])
如果调用这个函数,并向它提供一个包含姓名 Throatwobbler Mangrove
和年龄42(但不包含职业)的字典,
输出将如下:
Description of Throatwobbler Mangrove
Age: 42
如果在这个字典中添加职业camper,输出将如下:
Description of Throatwobbler Mangrove
Age: 42
Occupation: camper
这段代码很直观,但效率不高( 虽然这里的重点是代码简洁 ),
因为它必须两次查找 'occupation'
键:一次检查这个键是否存在(在条件中),
另一次获取这个键关联的值,以便将其打印出来。下面是另一种解决方案:
def describe_person(person):
print('Description of', person['name'])
print('Age:', person['age'])
try:
print('Occupation:', person['occupation'])
except KeyError:
pass
在这里,函数直接假设存在 'occupation'
键。如果这种假设正确,就能省点事:
直接获取并 打印值,而无需检查这个键是否存在。如果这个键不存在,
将引发 KeyError
异常,而 except
子句将捕获这个异常。
当发现,检查对象是否包含特定的属性时,try/except
也很有用。例如,
假设要检查 一个对象是否包含属性 write
,可使用类似于下面的代码:
#此代码会报错
# try:
# obj.write
# except AttributeError:
# print('The object is not writeable')
# else:
# print('The object is writeable')
在这里,try
子句只是访问属性 write
,而没有使用它来做任何事情。
如果引发了 AttributeError
异常,说明对象没有属性 write
,否则就说明有这个属性。
这种解决方案可替代使用 getattr
的解决方案,而且更自然。
具体使用哪种解决方案,在很大程度上取决于个人喜好。
请注意,这里在效率方面的提高并不大(实际上是微乎其微)。一般而言,除非程序存在性能
方面的问题,否则不应过多考虑这样的优化。关键是在很多情况下,相比于使用 if/else
,使用
try/except
语句更自然,也更符合Python的风格。因此应养成尽可能使用 try/except
语句的习惯。
如果只想发出警告,指出情况偏离了正轨,可使用模块 warnings
中的函数 warn
。
from warnings import warn
# warn("I've got a bad feeling about this.") #此代码会报错
警告只显示一次。如果再次运行最后一行代码,什么事情都不会发生。
如果其他代码在使用模块,可使用模块 warnings
中的函数 filterwarnings
来抑制发出的警告(或特定类型的警告),
并指定要采取的措施,如 error
或 ignore
。
from warnings import filterwarnings
filterwarnings("ignore")
warn("Anyone out there?")
filterwarnings("error")
# warn("Something is very wrong!") #此代码会报错
引发的异常为UserWarning
。发出警告时,可指定将引发的异常(即警告类别),但必须是Warning
的子类。
如果将警告转换为错误,将使用指定的异常。
另外,还可根据异常来过滤掉特定类型的警告。
filterwarnings("error")
# warn("This function is really old...", DeprecationWarning) #此代码会报错
filterwarnings("ignore", category=DeprecationWarning)
warn("Another deprecation warning.", DeprecationWarning)
# warn("Something else.") #此代码会报错
除上述基本用途外,模块 warnings
还提供了一些高级功能。
本章介绍了如下重要主题。
- 异常对象:异常情况(如发生错误)是用异常对象表示的。对于异常情况,有多种处理 方式;如果忽略,将导致程序终止。
- 引发异常:可使用
raise
语句来引发异常。它将一个异常类或异常实例作为参数, 但也可提供两个参数(异常和错误消息)。如果在except
子句中调用raise
时没有提供任何参数, 它将重新引发该子句捕获的异常。 - 自定义的异常类:可通过从
Exception
派生来创建自定义的异常。 - 捕获异常:要捕获异常,可在
try
语句中使用except
子句。在except
子句中,如果没有指 定异常类, 将捕获所有的异常。可指定多个异常类,方法是将它们放在元组中。如果向except
提供两个参数, 第二个参数将关联到异常对象。在同一条try/except
语句中,可包含多个except
子句,以便对不同的异常采取不同的措施。 else
子句:除except
子句外,还可使用else
子句,它在主try
块没有引发异常时执行。finally
:要确保代码块(如清理代码)无论是否引发异常都将执行,可使用try/finally
, 并将代码块放在finally
子句中。- 异常和函数:在函数中引发异常时,异常将传播到调用函数的地方(对方法来说亦如此)。
- 警告:警告类似于异常,但(通常)只打印一条错误消息。可指定警告类别。