Python中的简洁异常处理

4 投票
3 回答
770 浏览
提问于 2025-04-18 13:11

我不太喜欢“先看再跳”的编程方式,因为我觉得代码应该容易阅读。有时候,我无法预测会不会出错,比如资源是否可用或者内存是否足够。这种情况下,我还没找到一种既简洁又能处理这些问题的写法。

下面这个例子虽然勉强能看懂,但重复的代码是不能接受的。

try:
    myobject.write(filename)
except OSError:
    if prompt("%s is in use by another application." +
              "Close that application and try again.") == "Try again":
        myobject.write(filename) #repeated code

为了去掉重复的代码,我必须再加几行,并且把所有内容缩进,这样反而让代码更难读。

success = False
while not success:
    try:
        myobject.write(filename)
        success = True
    except OSError:
        if prompt("%s is in use by another application." +
                  "Close that application and try again.") != "Try again":
            break

有没有更简短的写法可以在Python中实现,而不重复代码呢?

3 个回答

1

你可以用一个装饰器类来包装这些函数,这个装饰器类可以接收参数作为输入,这样就能让异常处理变得更通用。

class exception_handler(object):

    def __init__(self, prompt_text, prompt_match, exception):
        self.prompt = prompt_text
        self.match = prompt_match
        self.exception = exception

    def __call__(self, f):
        def wrapped_f(*args, **kwargs):
            while True:
                try:
                    f(*args, **kwargs)
                    break
                except self.exception:
                    if raw_input(self.prompt) == self.match:
                        break       
        return wrapped_f


@exception_handler("your prompt (type 'quit' to exit): ", "quit", OSError)
def f(filename):
    print("before writing to file: {}".format(filename))
    # myobject.write(filename)
    raise OSError("testing...")
2

有没有更简短的写法可以在Python中实现这个功能,而不重复代码呢?

其实没有特别好的办法。你可以试试用一个 while True: 循环,这样就可以省去 success 这个变量:

while True:
    try:
        myobject.write(filename)
        break
    except OSError:
        if prompt("%s is in use by another application."
                  "Close that application and try again.") != "Try again":
            break

不过,这样写已经算是比较简洁了,同时还能保持代码的可读性。

另外,你会注意到我在if语句那一行去掉了 +。这是因为相邻的字符串会自动连接,所以不需要加这个符号。

6

除了把代码改成 while True 这种写法,你还可以添加一个叫 retry 的装饰器,然后把需要重试的代码放到一个用 retry 装饰的函数里:

from functools import wraps
from functools import update_wrapper


def retry(prompt_text="An error occured! Retry (y/n)?", 
          prompt_match='y',
          exception=Exception):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            while True:
                try:
                    ret = func(*args, **kwargs)
                    break
                except exception:
                    if raw_input(prompt_text) == prompt_match:
                        ret = None
                        break
            return ret 
        return update_wrapper(wrapper, func)
    return decorator

@retry(prompt_text="your prompt: ", prompt_match="quit", exception=OSError)
def do_write(myobject, filename):
    myobject.write(filename)

if __name__ == "__main__":
    myobject = ...
    filename = ...
    do_write(myobject, filename) # This will be retried.

不过,如果你只在一个地方用这个方法,那这样做可能就不太值得了。

撰写回答