“捕获”
指的是使用 try ... except
包裹特定语句,妥当的完成错误流程处理
raise
主动“抛出”异常
,更是优雅代码里必不可少的组成部分
要精确每个步骤的异常 step by step
永远只捕获
那些可能会抛出异常的语句块
尽量只捕获精确的异常类型
,而不是模糊的 Exception
# demo
from requests.exceptions import RequestException
def save_website_title(url, filename):
try:
resp = requests.get(url)
except RequestException as e:
print(f'save failed: unable to get page content: {e}')
return False
obj = re.search(r'<title>(.*)</title>', resp.text)
if not obj:
print('save failed: title tag not found in page content')
return False
title = obj.group(1)
try:
with open(filename, 'w') as fp:
fp.write(title)
except IOError as e:
print(f'save failed: unable to write to file {filename}: {e}')
return False
else:
return True
避免抽象级的异常被具体模块抛出
比如一个web服务端定义了一个抽象级总的 APIError
– 异常父类
还需要定义 具体的子异常 Error_xxx, Error_ooo, Error_xxo..
让模块
只抛出与当前抽象层级一致
的异常
# <PROJECT_ROOT>/util/image/processor.py
class ImageOpenError(Exception):
pass
def process_image(...):
try:
image = Image.open(fp)
except Exception as e:
raise ImageOpenError(exc=e)
... ...
# <PROJECT_ROOT>/app/views.py
def foo_view_function(request):
try:
process_image(fp)
except ImageOpenError:
raise error_codes.INVALID_IMAGE_UPLOADED
在必要的地方进行异常包装与转换
应该避免泄露低于当前抽象级别的异常
requests 模块 请求页面出错时所抛出的异常,不是它在底层所使用的 urllib3 模块的原始异常,
而是通过 requests.exceptions 包装过一次的异常
try:
requests.get('https://www.invalid-host-foo.com')
except Exception as e:
print(type(e))
# <class 'requests.exceptions.ConnectionError'>
避免扰乱核心代码 (使用上下文管理器
)
避免代码里充斥着大量的 try、except、raise
语句,让核心逻辑变得难以辨识
上下文管理器(context manager)
是一种配合 with
语句使用的特殊 Python 对象,
通过它,可以让异常处理工作变得更方便
# 根据error 统一具体的处理异常
from enum import IntEnum
class Common(Exception):
...
class TestError(Common):
def __init__(self, code):
self.code = code
...
class ErrorEnum(IntEnum):
SERVER_ERROR = 100000
TEST_ERROR = 100001
...
class ErrorManager:
#
def __init__(self, exception, error_code=ErrorEnum.SERVER_ERROR):
self.exception = exception
self.error_code = error_code
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
if exc_type is None:
return False
if exc_type == self.exception:
...
error_message = ErrorEnum(self.error_code).name
print(error_message)
# 做异常处理工作, log, 返回值
return True
if issubclass(exc_type, Exception):
...
print(exc_value)
error_message = f"SERVER_ERROR: {exc_value}"
print(error_message, traceback)
# 做异常处理工作, log, 返回值
return True
# 定义了一个名为 ErrorManager 的上下文管理器,
# 它在进入上下文时什么也不做。
# 但是在退出上下文时,会判断当前上下文中是否抛出了类型为 self.exception 的异常,
with ErrorManager(TestError, ErrorEnum.TEST_ERROR):
...
# 实际代码逻辑
# raise TestError(100001)
int("12312doasd")