Python中的简洁异常处理
我不太喜欢“先看再跳”的编程方式,因为我觉得代码应该容易阅读。有时候,我无法预测会不会出错,比如资源是否可用或者内存是否足够。这种情况下,我还没找到一种既简洁又能处理这些问题的写法。
下面这个例子虽然勉强能看懂,但重复的代码是不能接受的。
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.
不过,如果你只在一个地方用这个方法,那这样做可能就不太值得了。