对于Python异常处理慎用“except:pass”建议

翻译自StackOverflow中一个关于Python异常处理的问答。

问题:为什么“except:pass”是一个不好的编程习惯?

我时常在StackOverflow上看到有人评论关于except: pass的使用,他们都提到这是一个不好的Python编程习惯,应该避免。可我想知道为什么?有时候我并不在意出现的错误,而是只想让我的程序继续进行下去。就像这样:

try:
  something
except:
  pass

为什么这么使用except:pass不好?这背后的原因是什么,是不是因为这样我会放掉一些本该被处理的错误?还是这样我会捕获到所有类型的错误?

最佳回答:

正如你所猜测的那样,这么做的确有两个不好的地方。首先,因为没有指定任何异常类型,所以会捕获到任何类型的错误。其次,捕获到错误之后只会简单地让它通过而不是采取必要的处理措施。

我接下来的解释或许会有点长,所以将重点总结如下:
1. 不要将任意类型的错误作为捕获对象。必须明确你想要捕获的错误类型,并且写明只捕获它们。
2. 不要试图简单地敷衍错误处理动作。除非这么做是有目的的,但这通常都不太好。

那么接下来让我们更深入一些:

不要将任意异常作为捕获目标

当在代码中的某个地方使用异常捕获语句块时,你通常知道这个地方可能会抛出异常,并且你也知道这个地方可能会发生什么样的问题进而抛出何种异常,一旦异常被抛出,你将捕获到这个异常并使程序回到正轨上来。这就意味着你一定对这种异常有所准备,并能够在它发生的时候及时采取措施进行处理。

举个例子,你需要用户输入一个数字,并且使用int()函数将用户输入的字符串转换为整数类型,这时候你一定会想到如果输入的字符串并不是数字,那么就会发生值错误(ValueError)。如果真的发生了错误,那么你可以通过简单的让用户重新输入来让程序回到正轨,所以捕获值错误以及促使用户重新输入就是一个比较合理的处理策略。再举一个例子,如果你想从一个文件中读取配置信息,但正巧这个文件不存在。那么因为这是一个配置文件,如果它不存在你会返回一些默认的配置选项,所以这个文件就不是这么必要了。在这个例子中,捕获文件未找到错误(FileNotFoundError)以及返回默认配置项则是一个比较合适的处理策略。通过以上两个例子可以看到,我们都是在等待捕获特定的错误,并且针对每种错误都有特定的处理策略。

然而,如果我们在这里捕获所有的异常,那么为特定异常准备的那些处理策略就会因为遇到非正常类型的异常而失效,这将会使得正常的程序流程中断并且无法恢复。

让我们还是举配置文件的那个例子。正常的处理策略是如果发现文件并不存在,我们将使用默认的配置项,并可能在稍后决定是否将当前的配置项自动保存为配置文件(这样的话下一次文件就肯定存在了)。现在让我们假定我们捕获到了一个IsADirectoryError或是PermissionError错误,在这种情况下,我们可能不想让程序继续执行,我们仍然能够使用默认的配置参数,但是随后我们就不能保存文件了。也有可能用户希望使用自定义的配置项,所以这样的话就不能使用默认配置项了。所以我们这时候可能需要立即告知用户并停止当前程序。也有可能我们并不想在这么一小块代码中做这么多的事情,而是让应用层面的部分去关心它,所以我们也可能让这个错误浮到顶层,让顶层的业务逻辑去处理。

Python 2 idioms document文档中也提到了一个简单的例子:如果在我们的代码中出现了一个简单的拼写错误而导致程序错误。在这种情况下因为我们捕获所有的异常,所以我们将会捕获到名称错误(NameErrors)以及语法错误(SyntaxErrors)。两者都是常见的错误,并且两者都是不希望出现在我们最终代码中的。但是因为我们什么异常都逮,当异常发生时我们将无法区分具体的错误类型并且无法进行调试。

但是也存在这样一些并未预先准备的危险异常,诸如系统错误(SystemError)就很少发生以至于我们根本没有准备;这些异常通常需要更复杂的处理操作,这些操作通常可能会要求我们停止当前的工作。

在任何情况下,通过局部代码实现对所有异常的处理基本上都是不可能的,所以你应该有针对性的去处理那些经过准备的特定异常。有些人曾建议至少应该明确指明基本异常(Exception)这样的不包含诸如系统退出(SystemExit)和键盘中断(KeyboardInterrupt)这样设计用来终止应用程序的异常。但是我想说这样还是不够明确,并且我个人认为只有在一个地方才能仅仅只捕获Exception或是任何类型的异常,那就是一个单独的,应用程序层面的异常捕获器,并且这个捕获器唯一的任务就是去捕获任何可能出现的未经准备的漏网异常。这样的话我们仍然能够保留意外发生异常的相关信息作为进一步的代码扩展的依据(让然如果我们能让程序恢复的话),这样下一次我们就能够把这个异常在合适的地方显式地指定出来或是指导我们撰写测试用例以保证错误不再发生(当然了,这一切还是要当我们对特定异常有所准备时,没有准备的异常还是会溜掉)。

在异常处理的逻辑中,不要什么都不做

当显式地捕获到有限的几个异常之后,很多时候我们的确不需要做什么特别的处理。这种情况下,except SomeSpecificException: pass这么做是可以的。但是大多数情况下,我们还是需要一些与错误恢复相关的代码,例如重复尝试的动作以及设置默认值。

同时也考虑到其它情况,例如如果我们的代码结构已经确定了必须不断尝试直到成功才能继续,那么什么也不做就已经够了。具体来说,我们需要用户输入一个数字,因为我们知道用户可能不会按照我们设计的那样做,所以我们会将这个部分放入一个循环,比如像这样:

def askForNumber ():
  while True:
    try:
      return int(input('Please enter a number: '))
    except ValueError:
      pass

因为我们会不断让用户输入直到没有异常抛出为止,这种情况下我们就不需要在except块中做其他任何特别的操作,这样就够了。当然了,有人会说你至少应该让用户得到一些错误信息以明确他们为什么在此被反复地要求输入。

在其他一些情况下,except块中的passing语句显示了我们并没有真正的对异常做好准备。除非是一些简单的异常(诸如值错误ValueError或类型错误TypeError)我们都应该做一些操作,原因也很明显,避免简单的passing。如果真没什么可做的(如果你真的确定),那么考虑加一些解释性的注释在此;否则,请扩展except块添加一些恢复性的代码。

except: pass

最不能容忍的就是两者的结合了。这意味着我们自愿捕获任何异常(包括那些我们没有准备的)并且对它们视而不见。你应该至少在日志中记录一下这个错误并且向上提出来终止当前程序(我就不信出现MemoryError你的程序依然能正常运行)。放过这些异常将会使程序在错误的轨道上继续运行下去并且丢掉了关键的错误信息从而使得错误不易被发现,特别是当不是你亲自遇到它的时候。

所以,底线是去捕获那些经过准备的特定异常;其他发生的异常要么是等着你去修复的错误,要么是你无法处理的。当真的没有什么可做的时候放过某些特定异常是可以的,其他情况如果这么做就只能被认为是怠工或偷懒了。你的确应该去处理这些异常的。

(0)

相关推荐

  • Python中的pass语句使用方法讲解

    Python pass语句使用当语句要求不希望任何命令或代码来执行. pass语句是一个空(null)操作;在执行时没有任何反应.pass也是代码最终会是有用的,但暂时不用写出来(例如,在存根为例): 语法 Python pass语句语法如下: pass 例子 #!/usr/bin/python for letter in 'Python': if letter == 'h': pass print 'This is pass block' print 'Current Letter :', l

  • Python pass 语句使用示例

    Python pass是空语句,pass语句什么也不做,一般作为占位符或者创建占位程序,是为了保持程序结构的完整性,pass语句不会执行任何操作,比如: Python 语言 pass 语句语法格式如下: 复制代码 代码如下: pass 复制代码 代码如下: 实例: 复制代码 代码如下: #!/usr/bin/python for letter in 'Python':    if letter == 'h':      pass      print 'This is pass block'  

  • Python使用htpasswd实现基本认证授权的例子

    前面我讲解了如何将树莓派(Raspberry Pi)打造成无线路由,感觉每次通过命令ssh管理显麻烦,于是自己动手编写Web界面,主要是使用Python编写的CGI程序,这里用到了mini_httpd这款轻量的Web服务器,本来想装nginx的,但是想想还是精简一些吧,毕竟资源有限,况且Web管理界面仅我一个人访问. CGI应用跑起来了,但问题来了,如何实现普通路由的那种打开页面就弹出输入用户名密码的对话框? 这里主要用到HTTP协议的一个知识,那就是HTTP基本认证. 服务器端通过发送类似下面

  • Python pass详细介绍及实例代码

    Python pass的用法: 空语句 do nothing 保证格式完整 保证语义完整 以if语句为例,在c或c++/Java中: if(true) ; //do nothing else { //do something } 对应于Python就要这样写: if true: pass #do nothing else: #do something 1 pass语句在函数中的作用 当你在编写一个程序时,执行语句部分思路还没有完成,这时你可以用pass语句来占位,也可以当做是一个标记,是要过后来

  • qpython3 读取安卓lastpass Cookies

    之前我的博客写了python读取windows chrome Cookies,沿着同样的思路,这次本来想尝试读取安卓chrome Cookies, 但是可能是chrome的sqlite3版本比较高失败了,so改成读取lastpass 的Cookies. 背景介绍: qpython3 是一个基于sl4a实现的能让python3跑在安卓手机上集成环境. lastpass 是一个密码管理器,安卓版lastpass 内置了一个web浏览器.经分析lastpass的Cookies的表名,字段名与chrom

  • python中pass语句用法实例分析

    本文实例讲述了python中pass语句用法.分享给大家供大家参考.具体分析如下: 1.空语句 do nothing 2.保证格式完整 3.保证语义完整 4.以if语句为例: C/C++中写法: if(true) ; // do nothing else {} // do nothing python中写法: if true: pass # do nothing else: print "do something." 测试程序:定义一个空函数 >>> def null

  • 对于Python异常处理慎用“except:pass”建议

    翻译自StackOverflow中一个关于Python异常处理的问答. 问题:为什么"except:pass"是一个不好的编程习惯? 我时常在StackOverflow上看到有人评论关于except: pass的使用,他们都提到这是一个不好的Python编程习惯,应该避免.可我想知道为什么?有时候我并不在意出现的错误,而是只想让我的程序继续进行下去.就像这样: try: something except: pass 为什么这么使用except:pass不好?这背后的原因是什么,是不是因

  • Python异常处理操作实例详解

    本文实例讲述了Python异常处理操作.分享给大家供大家参考,具体如下: 一.异常处理的引入 >>>whileTrue: try: x = int(input("Please enter a number: ")) break exceptValueError: print("Oops! That was no valid number. Try again ") Please enter a number: y Oops!That was no

  • 编写python程序的90条建议

    1. 首先 建议1.理解 Pythonic 概念--详见 Python 中的<Python之禅> 建议2.编写 Pythonic 代码 (1)避免不规范代码,比如只用大小写区分变量.使用容易混淆的变量名.害怕过长变量名等.有时候长的变量名会使代码更加具有可读性. (2)深入学习 Python 相关知识,比如语言特性.库特性等,比如Python演变过程等.深入学习一两个业内公认的 Pythonic 的代码库,比如Flask等. 建议3:理解 Python 与 C 的不同之处,比如缩进与 {},单

  • 一文搞懂python异常处理、模块与包

    一 异常处理 1.什么是异常 Error(错误)是系统中的错误,程序员是不能改变的和处理的,如系统崩溃,内存空间不足,方法调用栈溢等.遇到这样的错误,建议让程序终止. Exception(异常)表示程序可以处理的异常,可以捕获且可能恢复.遇到这类异常,应该尽可能处理异常,使程序恢复运行,而不应该随意终止异常. 2常见异常 IndentationError: 缩进错误 KeyboardInterrupt: Ctrl+C被按下 UnboundLocalError : 有同名的全局变量 d = {'n

  • python异常处理之try finally不报错的原因

    因为有把python程序打包成exe的需求,所以,有了如下的代码 import time class LoopOver(Exception): def __init__(self, *args, **kwargs): pass class Spider: def __init__(self): super().__init__() def run(self): raise LoopOver @property def time(self): return '总共用时:{}秒'.format(se

  • Python异常处理与反射相关问题总结

    一.异常处理 在程序开发中如果遇到一些 不可预知的错误 或 你懒得做一些判断 时,可以选择用异常处理来做. import requests while True: url = input("请输入要下载网页地址:") res = requests.get(url=url) with open('content.txt', mode='wb') as f: f.write(res.content) 上述下载视频的代码在正常情况下可以运行,但如果遇到网络出问题,那么此时程序就会报错无法正常

  • Python异常处理如何才能写得优雅(retrying模块)

    目录 前言 不负责任版本 简单处理版本 改进处理版本 安装与使用 安装 使用 总结 前言 在写程序时,我们会经常碰到程序出现异常,这时候我们就不得不处理这些异常,以保证程序的健壮性. 处理异常的版本有以下几种,你通常的做法是哪种? 不负责任版本 这种情况下,不作任何处理,任由程序报错,从而导致程序中断. 针对简单的程序,这样做没什么问题,大不了我遇到问题之后把问题解决,然后重新运行.但是如果是复杂的系统就会很麻烦了,可能你一个异常阻塞了系统的运行,带来灾难性的后果. 简单处理版本 简单处理版本,

  • Python 异常处理的实例详解

    Python 异常处理的实例详解 与许多面向对象语言一样,Python 具有异常处理,通过使用 try...except 块来实现. Note: Python v s. Java 的异常处理 Python 使用 try...except 来处理异常,使用 raise 来引发异常.Java 和 C++ 使用 try...catch 来处理异常,使用 throw 来引发异常. 异常在 Python 中无处不在:实际上在标准 Python 库中的每个模块都使用了它们,并且 Python 自已会在许多不

  • 深入理解Python异常处理的哲学

    所谓异常指的是程序的执行出现了非预期行为,就好比现实中的做一件事过程中总会出现一些意外的事.异常的处理是跨越编程语言的,和具体的编程细节相比,程序执行异常的处理更像是哲学.限于认知能力和经验所限,不可能达到像解释器下import this看到的python设计之禅一样,本文就结合实际使用简单的聊一聊. 0. 前言 工作中,程序员之间一言不合就亮代码,毕竟不管是代码本身还是其执行过程,不会存在二义性,更不会含糊不清,代码可谓是程序员之间的官方语言.但是其处理问题的逻辑或者算法则并非如此. 让我至今

  • python异常处理和日志处理方式

    今天,总结一下最近编程使用的python异常处理和日志处理的感受,其实异常处理是程序编写时非常重要的一块,但是我一开始学的语言是C++,这门语言中没有强制要求使用try...catch语句,因此我通常编写代码的时候忽略了这一块,直到开始学习java的时候,发现好多时候编写代码必须加上try...catch 模块,然而我每次都不深入理解,仅仅使用eclipse自动补全功能加上try...catch模块,或者直接在类上加入throws Exception最省事,完全不用思考. 最近在编写python

随机推荐