我经常在其他Stack Overflow问题上看到关于如何不鼓励使用except: pass的评论。这为什么不好呢?有时我不在乎错误是什么,我只想继续写代码。
try:
something
except:
pass
为什么使用except: pass块不好?是什么让它变得糟糕?是我传递了一个错误还是我排除了任何错误?
我经常在其他Stack Overflow问题上看到关于如何不鼓励使用except: pass的评论。这为什么不好呢?有时我不在乎错误是什么,我只想继续写代码。
try:
something
except:
pass
为什么使用except: pass块不好?是什么让它变得糟糕?是我传递了一个错误还是我排除了任何错误?
当前回答
except:pass构造本质上是在运行try:块中包含的代码时,使出现的任何和所有异常条件保持沉默。
这种糟糕的做法是因为它通常不是你真正想要的。更常见的情况是,出现一些特定的情况,你想要保持沉默,除了:pass是一个太生硬的工具。它将完成工作,但它也会掩盖其他错误条件,您可能没有预料到,但可能非常希望以其他方式处理。
What makes this particularly important in Python is that by the idioms of this language, exceptions are not necessarily errors. They're often used this way, of course, just as in most languages. But Python in particular has occasionally used them to implement an alternative exit path from some code tasks which isn't really part of the normal running case, but is still known to come up from time to time and may even be expected in most cases. SystemExit has already been mentioned as an old example, but the most common example nowadays may be StopIteration. Using exceptions this way caused a lot of controversy, especially when iterators and generators were first introduced to Python, but eventually the idea prevailed.
其他回答
首先,它违背了Python的两个禅宗原则:
显性比隐性好 错误绝不能悄无声息地过去
它的意思是,你故意让你的错误悄无声息地过去。此外,您不知道究竟发生了哪个错误,因为except: pass将捕获任何异常。
其次,如果我们试图从Python的禅意中抽象出来,而只是从理智的角度来说话,你应该知道,使用except:pass会让你在系统中失去知识和控制。经验法则是,如果发生错误,就引发异常,并采取适当的操作。如果你事先不知道这些操作应该是什么,至少在某个地方记录错误(最好重新引发异常):
try:
something
except:
logger.exception('Something happened')
但是,通常情况下,如果您试图捕获任何异常,那么您可能正在做错误的事情!
第一个原因已经说过了——它隐藏了你没有预料到的错误。
(#2)——它使你的代码难以被其他人阅读和理解。如果你捕捉FileNotFoundException当你试图读取一个文件,那么它是相当明显的另一个开发人员'catch'块应该有什么功能。如果您没有指定异常,那么您需要额外的注释来解释该块应该做什么。
(#3) -它演示了惰性编程。如果您使用通用的try/catch,则表明您不了解程序中可能的运行时错误,或者您不知道Python中可能存在哪些异常。捕捉特定的错误表明您了解程序和Python抛出的错误范围。这更有可能使其他开发人员和代码审查人员信任您的工作。
那么,这段代码产生了什么输出呢?
fruits = [ 'apple', 'pear', 'carrot', 'banana' ]
found = False
try:
for i in range(len(fruit)):
if fruits[i] == 'apple':
found = true
except:
pass
if found:
print "Found an apple"
else:
print "No apples in list"
现在,想象一下try-except块是对复杂对象层次结构的数百行调用,并且本身是在大型程序的调用树中间调用的。当程序出问题时,你从哪里开始寻找?
通常,您可以将任何错误/异常分为以下三类之一:
Fatal: Not your fault, you cannot prevent them, you cannot recover from them. You should certainly not ignore them and continue, and leave your program in an unknown state. Just let the error terminate your program, there is nothing you can do. Boneheaded: Your own fault, most likely due to an oversight, bug or programming error. You should fix the bug. Again, you should most certainly not ignore and continue. Exogenous: You can expect these errors in exceptional situations, such as file not found or connection terminated. You should explicitly handle these errors, and only these.
在除了:pass之外的所有情况下,pass只会让你的程序处于未知状态,在这种状态下它会造成更多的损害。
为什么“except: pass”是一种糟糕的编程实践? 这为什么不好呢? 试一试: 某物 除了: 通过
这将捕获所有可能的异常,包括GeneratorExit、KeyboardInterrupt和SystemExit——这些异常可能是您不打算捕获的。这和捕获BaseException是一样的。
try:
something
except BaseException:
pass
旧版本的文档说:
由于Python中的每个错误都会引发异常,使用except:可以使许多编程错误看起来像运行时问题,从而阻碍调试过程。
Python异常层次结构
如果您捕获了一个父异常类,那么您也捕获了它们的所有子类。只捕获准备处理的异常要优雅得多。
下面是Python 3的异常层次结构——你真的想把它们都捕捉到吗?:
BaseException
+-- SystemExit
+-- KeyboardInterrupt
+-- GeneratorExit
+-- Exception
+-- StopIteration
+-- StopAsyncIteration
+-- ArithmeticError
| +-- FloatingPointError
| +-- OverflowError
| +-- ZeroDivisionError
+-- AssertionError
+-- AttributeError
+-- BufferError
+-- EOFError
+-- ImportError
+-- ModuleNotFoundError
+-- LookupError
| +-- IndexError
| +-- KeyError
+-- MemoryError
+-- NameError
| +-- UnboundLocalError
+-- OSError
| +-- BlockingIOError
| +-- ChildProcessError
| +-- ConnectionError
| | +-- BrokenPipeError
| | +-- ConnectionAbortedError
| | +-- ConnectionRefusedError
| | +-- ConnectionResetError
| +-- FileExistsError
| +-- FileNotFoundError
| +-- InterruptedError
| +-- IsADirectoryError
| +-- NotADirectoryError
| +-- PermissionError
| +-- ProcessLookupError
| +-- TimeoutError
+-- ReferenceError
+-- RuntimeError
| +-- NotImplementedError
| +-- RecursionError
+-- SyntaxError
| +-- IndentationError
| +-- TabError
+-- SystemError
+-- TypeError
+-- ValueError
| +-- UnicodeError
| +-- UnicodeDecodeError
| +-- UnicodeEncodeError
| +-- UnicodeTranslateError
+-- Warning
+-- DeprecationWarning
+-- PendingDeprecationWarning
+-- RuntimeWarning
+-- SyntaxWarning
+-- UserWarning
+-- FutureWarning
+-- ImportWarning
+-- UnicodeWarning
+-- BytesWarning
+-- ResourceWarning
不要这样做
如果你使用这种形式的异常处理:
try:
something
except: # don't just do a bare except!
pass
然后你就不能用Ctrl-C来中断你的something块了。您的程序将忽略try代码块中所有可能的异常。
下面是另一个同样有不良行为的例子:
except BaseException as e: # don't do this either - same as bare!
logging.info(e)
相反,尝试只捕获您知道正在寻找的特定异常。例如,如果你知道你可能会在一个转换中得到一个值错误:
try:
foo = operation_that_includes_int(foo)
except ValueError as e:
if fatal_condition(): # You can raise the exception if it's bad,
logging.info(e) # but if it's fatal every time,
raise # you probably should just not catch it.
else: # Only catch exceptions you are prepared to handle.
foo = 0 # Here we simply assign foo to 0 and continue.
用另一个例子进一步解释
您可能会这样做,因为您已经进行了web抓取并得到了一个UnicodeError,但因为您使用了最广泛的异常捕获,您的代码可能有其他基本缺陷,将试图运行到完成,浪费带宽,处理时间,磨损您的设备,耗尽内存,收集垃圾数据等。
如果其他人要求你完成,这样他们就可以依赖你的代码,我理解那种被迫处理所有事情的感觉。但如果你愿意在开发过程中遭遇失败,你就有机会纠正那些偶尔出现的问题,但这将是代价高昂的长期错误。
有了更精确的错误处理,代码就会更加健壮。