哪个更糟糕-代码重复或两次尝试/除外?

时间:2019-06-25 08:09:43

标签: python design-patterns

我遇到一种情况,我想在处理异常时做很多事情。由于我想就一般情况进行讨论,因此我将把我的具体情况翻译成更通用的语言。

当这段代码中有异常时,我想:

  1. 始终执行回滚式操作
  2. 如果是 特定于应用程序的异常,我想执行一些日志记录并吞下该异常。

所以我可以想到两种解决方法,都很丑陋:

# Method nested-try/except block
try:
    try:
        do_things()
    except:
        rollback()
        raise
except SpecificException as err:
    do_advanced_logging(err)
    return
# Method Duplicate Code
try:
    do_things()
except SpecificException as err:
    rollback()
    do_advanced_logging(err)
    return
except:
    rollback()
    raise

两者的行为相同。

我自己正在尝试嵌套的try / except解决方案。尽管速度可能会稍慢一些,但我认为速度差异在这里并不重要-至少与我的具体情况无关。我想避免重复代码,这也是因为我的rollback()语句比数据库回滚涉及的更多,即使它具有完全相同的目的(涉及到Web-API)。

还有没有发现的第三个更好的选择吗?还是重复代码方法更好?请注意,rollback()功能已被尽可能地排除,但仍包含一个函数调用和三个参数,其中包括一个硬编码的字符串。由于此字符串是唯一的,因此没有理由将其命名为常量。

3 个答案:

答案 0 :(得分:7)

如何检查代码中的异常实例类型?

# Method .. No Duplicate Code
try:
    do_things()
except Exception as e:
    rollback()
    if isinstance(e, SpecificException):
        do_advanced_logging(e)
        return
    raise

答案 1 :(得分:4)

如何将回滚放在finally子句中?像这样:

do_rollback = True
try:
    do_things()
    do_rollback = False
except SpecificException as err:
    do_advanced_logging(err)
finally:
    if do_rollback:
        rollback()

一种替代方法是使用else子句,这将使您在非异常情况下执行更多操作,并且不会将所有异常都捕获在同一位置:

do_rollback = True
try:
    do_things()
except SpecificException as err:
    do_advanced_logging(err)
else:
    record_success()
    do_rollback = False
finally:
    if do_rollback:
        rollback()

record_success可以举起SpecificException但却不想do_advanced_logging的情况下很有用

答案 2 :(得分:2)

您可以编写一个上下文管理器:

import random

class SpecificException(Exception):
    pass

def do_things(wot=None):
    print("in do_things, wot = {}".format(wot))
    if wot:
        raise wot("test")


def rollback():
    print("rollback")


def do_advance_logging(exc_type, exc_val, traceback):
    print("logging got {} ('{}')".format(exc_type, exc_val))


class rollback_on_error(object):
    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, traceback):
        # always rollback
        rollback()
        # log and swallow specific exceptions
        if exc_type and issubclass(exc_type, SpecificException):
            do_advance_logging(exc_type, exc_val, traceback)
            return True
        # propagate other exceptions
        return False


def test():
    try:
        with rollback_on_error():
            do_things(ValueError)
    except Exception as e:
        print("expected ValueError, got '{}'".format(type(e)))
    else:
        print("oops, should have caught a ValueError")

    try:
        with rollback_on_error():
            do_things(SpecificException)
    except Exception as e:
        print("oops, didn't expect exception '{}' here".format(e))
    else:
        print("ok, no exception")


    try:
        with rollback_on_error():
            do_things(None)
    except Exception as e:
        print("oops, didn't expect exception '{}' here".format(e))
    else:
        print("ok, no exception")



if __name__ == "__main__":
    test()

但是除非您有十多次这种模式出现,否则我宁愿使用非常明显且完美的pythonic解决方案-嵌套异常处理程序或except子句中的显式类型检查(isinstance)。