Python中的Try/Except:如何避免重复?

7

我有一个类,可以将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
< p > 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'

选项三:使用装饰器(由@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'

哪个选项被认为是最符合Python风格的?还有更好的选择吗?

就我个人而言,我会使用装饰器来包装异常处理程序中的任何函数。在我看来,装饰器的意图比每次调用都包装更清晰,但这绝不是基于事实的。 - Zach Gates
我同意@ZachGates的观点,为什么不直接使用装饰器呢? - Sraw
@ZachGates 很好的观点。装饰器绝对是一个选项。代码流程与基线代码不完全相同,因为在API调用之前和之后的“stuff”部分将包含在try/except处理程序中。但这在这里不是问题,因为try/except处理程序仅捕获API异常,而不是“stuff”部分可能引发的异常。我已经在问题陈述中添加了装饰器方法。谢谢! - lacton
1个回答

1
这是什么意思?
class ApiWrapper(object):

    def __init__(self, api):
        self._api = api

    def api_call(self, methodname, *args, **kwargs):
        method = getattr(self._api, methodname)
        try:
            return method(*args, **kwargs)
        except ApiException:
            print 'ApiWrapper caught an ApiException. Doing complicated error handling logic. Raising a different exception.'
            raise ApiWrapperException

    def do_one_thing(self):
        print 'do_one_thing stuff before API call'
        self.api_call("do_one_thing")
        print 'do_one_thing stuff after API call'

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接