关于python:如何正确地忽略异常

How to properly ignore exceptions

当您只是想尝试—除非不处理异常,否则如何在Python中进行尝试?

下面的方法正确吗?

1
2
3
4
try:
    shutil.rmtree(path)
except:
    pass


1
2
3
4
try:
  doSomething()
except:
  pass

1
2
3
4
try:
  doSomething()
except Exception:
  pass

不同之处在于,第一个捕获器还将捕获KeyboardInterruptSystemExit之类的对象,这些对象直接派生自exceptions.BaseException,而不是exceptions.Exception。详见文档:

试着声明本;http://docs.python.org/reference/compound_stmts.html#try异常本;http://docs.python.org/library/exceptions


通常认为最好的实践是只捕获您感兴趣的错误。在shutil.rmtree的情况下,它可能是OSError:

1
2
3
4
>>> shutil.rmtree("/fake/dir")
Traceback (most recent call last):
    [...]
OSError: [Errno 2] No such file or directory: '/fake/dir'

如果你想悄悄地忽略这个错误,你可以这样做:

1
2
3
4
try:
    shutil.rmtree(path)
except OSError:
    pass

为什么?假设您(以某种方式)意外地将一个整数而不是字符串传递给函数,比如:

1
shutil.rmtree(2)

它将给出错误"TypeError: cocing to Unicode: need string or buffer, int found"—您可能不想忽略这一点,这可能很难调试。

如果您确实想忽略所有错误,请捕获Exception而不是一个简单的except:语句。再一次,为什么?

不指定异常捕获每个异常,包括SystemExit异常,例如sys.exit()使用:

1
2
3
4
5
6
>>> try:
...     sys.exit(1)
... except:
...     pass
...
>>>

将其与下面正确退出的代码进行比较:

1
2
3
4
5
6
>>> try:
...     sys.exit(1)
... except Exception:
...     pass
...
shell:~$

如果你想写更好的行为代码,OSError异常可以表示各种错误,但在上面的例子中,我们只想忽略Errno 2,所以我们可以更具体:

1
2
3
4
5
6
7
8
9
try:
    shutil.rmtree(path)
except OSError, e:
    if e.errno == 2:
        # suppress"No such file or directory" error
        pass
    else:
        # reraise the exception, as it's an unexpected error
        raise

您还可以将import errnoif更改为if e.errno == errno.ENOENT:


When you just want to do a try catch without handling the exception, how do you do it in Python?

这取决于你所说的"处理"是什么意思。

如果您打算在不采取任何操作的情况下捕获它,那么您发布的代码将会工作。

如果你的意思是你想对异常采取行动,而不阻止异常上升堆栈,那么你想要这样的东西:

1
2
3
4
5
try:
    do_something()
except:
    handle_exception()
    raise  #re-raise the exact same exception that was thrown

首先,我从这篇文章中引用了杰克·奥康纳的答案。被引用的线程被关闭,所以我写在这里:

在Python 3.4中有一种新的方法可以做到这一点:

1
2
3
4
from contextlib import suppress

with suppress(Exception):
    # your code

下面是添加它的commit: http://hg.python.org/cpython/rev/406b47c64480

这是作者Raymond Hettinger,他在讨论这个问题以及其他各种Python热:https://youtu.be/OSGv2VnC0go?t=43m23s

我添加的是Python 2.7的等效物:

1
2
3
4
5
6
7
8
from contextlib import contextmanager

@contextmanager
def ignored(*exceptions):
    try:
        yield
    except exceptions:
        pass

然后像在Python 3.4中那样使用它:

1
2
with ignored(Exception):
    # your code

完整性:

1
2
3
4
5
6
7
8
9
>>> def divide(x, y):
...     try:
...         result = x / y
...     except ZeroDivisionError:
...         print"division by zero!"
...     else:
...         print"result is", result
...     finally:
...         print"executing finally clause"

来自python教程。

还请注意,您可以像这样捕获异常:

1
2
3
4
>>> try:
...     this_fails()
... except ZeroDivisionError as detail:
...     print 'Handling run-time error:', detail

How to properly ignore Exceptions?

有几种方法可以做到这一点。

然而,示例的选择有一个简单的解决方案,不涉及一般情况。

特定于示例:

而不是

1
2
3
4
try:
    shutil.rmtree(path)
except:
    pass

这样做:

1
shutil.rmtree(path, ignore_errors=True)

这是一个特定于shutil.rmtree的参数。通过执行以下操作,您可以看到对它的帮助,您还将看到它还允许对错误执行功能。

1
2
>>> import shutil
>>> help(shutil.rmtree)

由于这只涵盖了示例的有限情况,所以我将进一步演示如何处理不存在这些关键字参数的情况。

一般方法

由于上面只讨论了示例的有限情况,所以我将进一步演示如何处理不存在这些关键字参数的情况。

New in python3.4:

您可以导入suppress上下文管理器:

1
from contextlib import suppress

但只抑制最特殊的例外:

1
2
with suppress(FileNotFoundError):
    shutil.rmtree(path)

您将无声地忽略一个FileNotFoundError:

1
2
3
4
>>> with suppress(FileNotFoundError):
...     shutil.rmtree('bajkjbkdlsjfljsf')
...
>>>

从文档:

As with any other mechanism that completely suppresses exceptions,
this context manager should be used only to cover very specific errors
where silently continuing with program execution is known to be the
right thing to do.

注意,suppressFileNotFoundError只在python3中可用。

如果您希望您的代码也能在python2中工作,请参见下一节:

Python 2,3:

When you just want to do a try/except without handling the exception,
how do you do it in Python?

Is the following the right way to do it?

1
2
3
4
try :
    shutil.rmtree ( path )
except :
    pass

对于Python 2兼容的代码,pass是使语句成为no-op的正确方法。但是当您执行一个简单的except:时,这与执行包含GeneratorExitKeyboardInterruptSystemExitexcept BaseException:是一样的,而且通常您不想捕获这些东西。

事实上,在命名异常时应该尽可能地具体。

下面是Python(2)异常层次结构的一部分,正如您所看到的,如果您捕捉到更多的一般异常,您可以隐藏您没有预料到的问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- StopIteration
      +-- StandardError
      |    +-- BufferError
      |    +-- ArithmeticError
      |    |    +-- FloatingPointError
      |    |    +-- OverflowError
      |    |    +-- ZeroDivisionError
      |    +-- AssertionError
      |    +-- AttributeError
      |    +-- EnvironmentError
      |    |    +-- IOError
      |    |    +-- OSError
      |    |         +-- WindowsError (Windows)
      |    |         +-- VMSError (VMS)
      |    +-- EOFError
... and so on

您可能想在这里捕获一个OSError,可能您不关心的异常是是否没有目录。

我们可以从errno库中获得特定的错误号,如果没有这个错误号,则重新执行:

1
2
3
4
5
6
7
8
9
import errno

try:
    shutil.rmtree(path)
except OSError as error:
    if error.errno == errno.ENOENT: # no such file or directory
        pass
    else: # we had an OSError we didn't expect, so reraise it
        raise

注意,裸升会引发原始异常,在本例中,这可能正是您想要的。写得更简洁,因为我们真的不需要显式的pass与代码在异常处理:

1
2
3
4
5
try:
    shutil.rmtree(path)
except OSError as error:
    if error.errno != errno.ENOENT: # no such file or directory
        raise


When you just want to do a try catch without handling the exception,
how do you do it in Python?

这将帮助您打印异常是什么:(即在不处理异常的情况下尝试catch并打印异常)。

1
2
3
4
5
import sys
try:
    doSomething()
except:
    print"Unexpected error:", sys.exc_info()[0]

1
2
3
4
5
6
7
try:
      doSomething()
except Exception:
    pass
else:
      stuffDoneIf()
      TryClauseSucceeds()

仅供参考,else子句可以放在所有异常之后,并且只有在try中的代码没有引起异常时才会运行。


在Python中,我们处理与其他语言类似的异常,但是不同之处在于一些语法上的不同,例如,

1
2
3
4
5
6
7
8
try:
    #Your code in which exception can occur
except <here we can put in a particular exception name>:
    # We can call that exception here also, like ZeroDivisionError()
    # now your code
# We can put in a finally block also
finally:
    # Your code...

我需要忽略多个命令中的错误,而fuckit做到了

1
2
3
4
5
6
7
8
9
10
11
import fuckit

@fuckit
def helper():
    print('before')
    1/0
    print('after1')
    1/0
    print('after2')

helper()


简单地提出相关的异常,就像这样:

1
2
3
4
5
try:
     raise NameError('Joan')
 except NameError:
     print 'An exception just raised again by Joan!'
     raise

就这么简单。:)

要了解更多细节,请阅读本文:https://docs.python.org/3.6/tutorial/errors.html