【发布时间】:2019-02-19 11:00:04
【问题描述】:
我的团队内部有一个使用自动化脚本的内部代码块的争论。
以下函数发出网络请求并返回它得到的结果。
网络请求有时会因为 IOError 而失败,但如果代码重试,它将能够连接并再次获得结果。所以代码有一个重试机制来处理这样的错误。
但有时会发生其他错误(例如超时或其他错误)。从需求的角度来看,对于此类问题,我们应该以错误代码退出脚本,以便调用程序知道脚本有问题,并说邮件工程师让他们处理。
代码如下:
def my_func():
retry_times = 5
sleep_time = 5
for i in range(0, 6)
try:
result = network_request()
break
except IOError as err:
if i == retry_times:
log.error
raise
else:
log.warning("Request failed, sleep %s seconds and retry <%s/%s>"
% (sleep_time, i, retry_times))
return result
但是我们的一位团队成员认为,我们应该捕获所有剩余的异常,永远不要让异常被抛出到最外层的代码,最后让脚本以错误代码退出并打印堆栈跟踪打印。
他称之为“异常泄漏”,这违反了常见的编码规则。这是他的代码(带有“# added code line”的行是更改的行):
def my_func():
retry_times = 5
sleep_time = 5
for i in range(0, 6)
try:
result = network_request()
break
except IOError as err:
if i == retry_times:
log.error
raise
else:
log.warning("Request failed, sleep %s seconds and retry <%s/%s>"
% (sleep_time, i, retry_times))
except Exception: # Added code line
log.error("Error happened: %s" % err) # Added code line
exit(1)
return result
我说他在做 Python 刚刚做过的事情:
- 打印错误日志。
- 存在具有非零退出代码的脚本
但他争辩说:“你怎么知道 Python 在捕获异常时什么也没做?”、“留下未捕获的异常会导致内存泄漏”、“任何设计良好的程序都不会将未捕获的异常留到最后,它是常识”。
这让我很困惑,因为我从未听说过这种“常识”,尤其是对于 Python(不确定 C++ 是否具有这种“常识”)。我用谷歌搜索了这个话题,发现甚至没有人问过这样的问题。我从一开始就知道异常处理的原理是:
- 处理您的代码能够处理的异常。
- 如果您无法处理这些异常,请向外部范围提出这些异常。
在这种情况下,由于该脚本是内部使用自动化脚本,当发生此类问题时,打印详细的堆栈跟踪并以非零错误代码退出脚本正是我们想要的,导致代码无法处理此类问题所以它应该最终向工程师提出这个问题。
添加捕获未知异常的行,打印错误日志,然后以非零错误代码退出脚本,就像重复地做已经完成的事情。
所以我在这里问: 1.“将未捕获的异常留到最后并最终导致脚本退出”会导致任何诸如“内存泄漏”之类的问题吗? 2. Python编码规则中是否有这样的“常识”,不留下任何未捕获的异常,提供“打印错误日志”和“以非零代码退出”正是脚本想要做的。
【问题讨论】:
-
未捕获的异常会终止程序,所以谁在乎呢?不咳嗽泛型异常(如
Exception)是一种很好的编程风格。 -- 通过退出程序,内核将释放所有资源(内存、文件、网络等) -- BTW 内存分配应该是最小的,所以它只是内部内存分配,可能永远不会要求内核分配其他大内存用于后续内存分配的缓冲区) -
不相关,但你不应该在
try块内return result避免重复network_request()吗?无论如何,对由于未捕获的异常导致的内存泄漏持怀疑态度。未捕获的异常只会终止程序,程序运行时会发生泄漏,并且不会释放不需要的资源。 -
@nicola 原始程序在 network_request() 之后确实有一个“中断”,但我错过了在这里添加它(我现在才添加它)所以不用担心。
标签: python memory-leaks exception-handling coding-style