什么是异常:
异常理解为在程序正常的执行过程中,出现的不属于正常控制流的其他情况。
导致异常发生的情况是由于未在预期范围内的是执行时出现的中断,未在预期范围内的情况有很多,比如对于是调用指定文件或者目录时对于不存在情况的异常,算为可控范围,还有使用request模块时,发请求时遇到的各类连接失败问题,异常导致中断,所以合理对异常的处理,可以在执行前扩大对代码可以正常执行的环境,对这种可能会导致程序中断退出的内容,都需要妥善处理,从而来增强代码的健壮性。
异常的处理:
对于异常的处理,可以从以下两个步骤进行考虑
步骤:
检测是否发生预期异常
为预期发生的异常进行操作,保证程序在可控范围内执行 **
检测到异常,重要的如何解决异常的问题,能够保证程序正常执行,或者为最低影响程序正常执行,也可以自定义异常,将不符合预期要求的程序的结果通过捕获,抛出,或者执行语句
操作有以下:
忽略异常,记录异常信息到日志
执行操作,能够保证程序正常执行,或者为最低影响程序正常执行
记录异常信息,抛出,中断程序(可以不捕获这类异常,当异常发生后直接使程序中断,但是不会记录在日志中)
1. 抛出异常
用已有的异常的类标记抛出
标准输出格式:
raise MyException("Error message")
自定义异常类(https://blog.csdn.net/skullFang/article/details/78820541)
用于在主动需要抛出的异常,抛出异常中断程序
# 自定义异常
class CmdbRequestPostError(Exception):
pass
def request_data_from_cmdb(cmdb_storage_api, data):
get_params, body, headers = signature_auth(data, cmdb_storage_secret)
url = CMDB_BASIC_API + cmdb_storage_api + "?" + get_params
retry = 0
while True: # 只捕获requests.exceptions.Timeout得异常,当捕获到时,进行request重试,重试5次后仍然为该异常,则抛出异常,退出程序
try:
r = requests.post(url, data=body, headers=headers)
break
except requests.exceptions.Timeout as e:
logger.warn(
repr(e) + ' while send post request to {url}'.format(url=url))
retry += 1
time.sleep(10)
if retry == 5:
logger.error(repr(e) +
' The maximum number of retries has been exceeded 5, while retries the post request to {url}'.format(url=url))
raise
_msg = 'api: ' + CMDB_BASIC_API + cmdb_storage_api + ' return: ' + r.text
# r.text's format is like this:
# '{"code":0,"data":[]}'
# When the code is not 0, the format like this:
# '{"code":401}'
result = utfjson.load(r.text)
# 由于不符合0的返回码会导致后续的程序的中断,所以自定义异常进行抛出,并记录到日志
if result["code"] == 0:
logger.info(_msg)
return result
else:
logger.error(_msg)
raise CmdbRequestPostError(
_msg + ' The {result} is not 0 '.format(result=result))
2. 捕获异常及处理
1)发生异常不想中断,执行异常发生后才会执行的操作,保证程序进行下去的用法:
try:
<语句> #运行别的代码
except <名字> as <数据>:
<语句> #如果引发了'name'异常,执行操作
else:
<语句> #如果没有异常发生
# Example:
...
try: # try中只放可能会发生异常的语句
old_applications = os.listdir(prometheus_file_sd_dir)
except OSError as e: # 对捕获到的异常如果是OSError,则执行以下语句
logger.exception(repr(e) + ' while list the dirs in {prometheus_file_sd_dir}'.format(
prometheus_file_sd_dir=prometheus_file_sd_dir))
else: # 对于未捕获到异常时,程序执行以下语句
for old_application in old_applications:
if old_application not in applications:
fsutil.remove(
prometheus_file_sd_dir + '{old_application}'.format(old_application=old_application))
2)捕获到异常后执行,重复抛出异常,不常用
try:
raise NameError
except NameError as e:
logger.error('Error message') # 可以记录异常信息
e.args += ('more info',) # 可以对异常的抛出信息增加描述
raise #raise后不加任何字符
3)不管是否发生异常,必须执行的操作,try…finally
try:
<语句>
except <name> as <数据>:
<语句>
finally:
<语句> #退出try时总会执行,当捕获到异常后直接跳到finally,执行完finally的内容后,再回去再次触发异常,执行异常触发执行的语句
在捕获异常后,必须要有except或者finally进行处理,否则try毫无意义
原则:
- 只处理可以预期到的异常,捕获然后执行语句使得不影响程序执行 抛出的异常应该说明原因,常用 logger.error(‘Errormessage’)
- try中只执行有可能发生异常的语句,不需要其他没有捕获意义的语句try中只执行有可能发生异常的语句,不需要其他没有捕获意义的语句
- 不要使用异常来控制流程,比如故意指定某个异常,然后用except执行正常程序外的内容
- 如果有需要,切记使用finally来释放资源
- 如果有需要,请不要忘记在处理异常后做清理工作或者回滚操作,比如使用f.close()
好的文章:
异常的用法及规则:https://zhuanlan.zhihu.com/p/33454825
google python 风格指南:https://zh-google-styleguide.readthedocs.io/en/latest/google-python-styleguide/python_language_rules/#id3
对于重复抛出异常的用法:http://www.ianbicking.org/blog/2007/09/re-raising-exceptions.html
xp要求的内容:https://github.com/baishancloud/learn/blob/master/code-style/python.md