【发布时间】:2018-02-10 04:32:39
【问题描述】:
我有一个类可以将 API 引发的低级异常转换为高级异常。这个类充满了复杂的、重复的错误处理逻辑。我正在寻找减少这种重复的pythonic方法。
这是一个人为的例子。
class ApiWrapperException(Exception):
pass
class ApiWrapper(object):
def __init__(self, api):
self._api = api
def do_one_thing(self):
print 'do_one_thing stuff before API call'
try:
self._api.do_one_thing()
except ApiException:
print 'ApiWrapper caught an ApiException. Doing complicated error handling logic. Raising a different exception.'
raise ApiWrapperException
print 'do_one_thing stuff after API call'
def do_another_thing(self):
print 'do_another_thing stuff before API call'
try:
self._api.do_another_thing()
except ApiException:
print 'ApiWrapper caught an ApiException. Doing complicated error handling logic. Raising a different exception.'
raise ApiWrapperException
print 'do_another_thing stuff after API call'
在此示例中,ApiWrapper 类将低级 ApiException 转换为更好的 ApiWrapperException。但是有很多重复。
选项 1:使用函数包装器
我可以将重复的代码放在一个内部函数中,如下所示:
def handle_api_errors(api_callable):
def call(*args, **kwargs):
try:
return api_callable(*args, **kwargs)
except ApiException:
print 'ApiWrapper caught an ApiException. Doing complicated error handling logic. Raising a different exception.'
raise ApiWrapperException
return call
ApiWrapper 类简化为:
class ApiWrapper(object):
def __init__(self, api):
self._api = api
def do_one_thing(self):
print 'do_one_thing stuff before API call'
handle_api_errors(self._api.do_one_thing)()
print 'do_one_thing stuff after API call'
def do_another_thing(self):
print 'do_another_thing stuff before API call'
handle_api_errors(self._api.do_another_thing)()
print 'do_another_thing stuff after API call'
选项 2:使用上下文管理器
我可以将重复的代码放在上下文管理器中,如下所示:
@contextlib.contextmanager
def handle_api_errors():
try:
yield
except ApiException:
print 'ApiWrapper caught an ApiException. Doing complicated error handling logic. Raising a different exception.'
raise ApiWrapperException
ApiWrapper 类简化为:
class ApiWrapper(object):
def __init__(self, api):
self._api = api
def do_one_thing(self):
print 'do_one_thing stuff before API call'
with handle_api_errors():
self._api.do_one_thing()
print 'do_one_thing stuff after API call'
def do_another_thing(self):
print 'do_another_thing stuff before API call'
with handle_api_errors():
self._api.do_another_thing()
print 'do_another_thing stuff after API call'
选项 3:使用装饰器(由 @ZachGates 建议)
我可以将重复的代码放在装饰器中,如下所示:
def handle_api_errors(api_calling_func):
def decorated_func(*args, **kwargs):
try:
api_calling_func(*args, **kwargs)
except ApiException:
print 'ApiWrapper caught an ApiException. Doing complicated error handling logic. Raising a different exception.'
raise ApiWrapperException
return decorated_func
ApiWrapper 类简化为:
class ApiWrapper(object):
def __init__(self, api):
self._api = api
@handle_api_errors
def do_one_thing(self):
print 'do_one_thing stuff before API call'
self._api.do_one_thing()
print 'do_one_thing stuff after API call'
@handle_api_errors
def do_another_thing(self):
print 'do_another_thing stuff before API call'
self._api.do_another_thing()
print 'do_another_thing stuff after API call'
哪个选项被认为是最 Pythonic 的?有更好的选择吗?
【问题讨论】:
-
就我个人而言,我会使用装饰器来包装任何作为异常处理程序的函数。在我看来,装饰器背后的意图比包装每个调用更清晰,但这绝不是基于事实。
-
我同意@ZachGates,你为什么不直接使用装饰器?
-
@ZachGates 好点。装饰器绝对是一种选择。代码流不会与基线代码中的严格相同,因为 API 调用之前和之后的“东西”部分将包含在 try/except 处理程序中。但这不是问题,因为 try/except 处理程序只捕获 API 异常,而不是“东西”部分可能引发的异常。我在问题陈述中添加了装饰器方法。谢谢!
标签: python python-decorators contextmanager