如何编写一个捕获所有异常的`try`/`except`块?

1121

我该如何编写一个能够捕获所有异常的 try/except 块?


6
如果你试图捕获任何异常,那么大多数情况下,你可能做错了什么。我的意思是,在你的代码中可能会有拼写错误,但你甚至都不知道。最好的做法是捕获具体的异常。 - vwvolodya
36
更准确地说,只有当异常被静默地捕获时才会成为问题。很难想象在哪些情况下这种方法是合适的,除非捕获的错误消息被打印到sys.stderr并可能被记录。这是一种完全有效和常见的异常处理方式。 - Evgeni Sergeev
1
你有尝试过以下代码吗:try: whatever() except Exception as e: exp_capture() ? - Charlie Parker
显示剩余2条评论
10个回答

1311

除了一个裸露的 except: 从句(正如其他人所说,你不应该使用),你可以简单地捕获 Exception

import traceback
import logging

try:
    whatever()
except Exception as e:
    logging.error(traceback.format_exc())
    # Logs the error appropriately. 

如果您想在终止之前处理任何未捕获的异常,通常只会在代码最外层考虑这样做。

except Exception 相对于裸的 except 的优势在于它不会捕获一些异常,其中最明显的是 KeyboardInterruptSystemExit:如果您捕获并忽略了它们,那么可能会使其他人难以退出您的脚本。


8
对于任何想知道的人来说,完全出乎我的意料,这仍然会捕获非异常子类化的内容,比如在Python 2.x中的整数(int)。 - Joseph Garvin
11
@JosephGarvin,那是不正确的,也就是说这样做无法捕获不继承“Exception”的“非异常”情况。请注意,不可能将"int"作为异常抛出,尝试这样做会引发"TypeError"异常,在这种情况下包含的"except Exception"子句会捕获它。另一方面,旧式类可以被抛出并且符合不继承“Exception”的“非异常”条件 - 这将被裸的"except"子句捕获,但不会被"except Exception"子句捕获。 - Yoel
4
@JosephGarvin 请查看这篇博客文章:https://chris-lamb.co.uk/posts/no-one-expects-string-literal-exception我和 @Yoel 的想法一致,你的测试只是掩盖了 TypeError - Duncan
2
@CharlieParker 如果你想要捕获它们,那么没有什么问题,但通常情况下你不需要这样做。调用 sys.exit() 通常意味着你希望应用程序终止,但如果你捕获了 SystemExit,它就不会终止。同样地,如果你在运行脚本时按下 control-C(Windows 上的 Ctrl-break),你期望程序停止,而不是捕获错误并继续执行。但如果你想在退出之前进行清理,你可以捕获其中任何一个或两个。 - Duncan
3
@CharlieParker 你可以尝试 except BaseException as e: notify_user(e); raise 这样就能捕获所有异常并进行任何所需的通知,但我不了解HPC,所以你可能需要发布一个新的SO问题来询问。 - Duncan
显示剩余10条评论

752

你可以这样做,但最好不要:

try:
    do_something()
except:
    print("Caught it!")

不过,这也会捕获像KeyboardInterrupt这样的异常,你通常不希望这样做,对吧?除非您立即重新引发异常 - 参见以下示例来自文档

try:
    f = open('myfile.txt')
    s = f.readline()
    i = int(s.strip())
except IOError as (errno, strerror):
    print("I/O error({0}): {1}".format(errno, strerror))
except ValueError:
    print("Could not convert data to an integer.")
except:
    print("Unexpected error:", sys.exc_info()[0])
    raise

34
可能的解决方法:http://effbot.org/zone/stupid-exceptions-keyboardinterrupt.htm - Mikel
9
你真的应该将输出打印到stderr。 - alexia
80
我非常非常强烈地不同意这个说法,“不应该”。你应该尽量少用它。在处理第三方库(有时是动态加载的!)时,它们可能会出现大量异常,跟踪所有异常可能是一项非常痛苦的任务,如果你错过了其中一个,你的系统就会出现一个非常严重的问题。话虽如此,最好尽可能跟踪并适当处理尽可能多的异常,然后为你错过的异常设置备用 catch all。 - Blaze
56
我觉得奇怪的是,在一种鸭子类型语言中,你不需要声明实例变量,但却突然非常关注你的所有异常不进行类型标注。嗯! - Blaze
9
仍然缺少except Exception: - Pedro Gordo
显示剩余9条评论

178

7
按下 Ctrl-C 后希望保存长时间运行作业的进度,这种情况是否不寻常? - BallpointBen
我有我的工作从HPC管理器运行。我想捕获(slurm、qsub、condor)管理器何时退出(通过我的自定义电子邮件向自己发送电子邮件)。except:会捕获到吗?但是它没有给我e的句柄:( - Charlie Parker
1
在Python中,所有的异常都必须是从BaseException派生的类的实例,但如果你可以在一般情况下省略它-省略它,问题是,代码检查工具会发出警告。 - jave.web
@BallpointBen 这就是信号处理程序的作用。它是 SIGINT 的默认信号处理程序,最开始会引发 KeyboardInterrupt。当然,你可以捕获 KeyboardInterrupt,但这只是许多可能过早终止程序的信号之一。其中一些不会生成任何类型的异常,因此最好统一处理所有信号。 - chepner
2
警告:像这样使用BaseException通常不是您想要的。 您真的想捕获键盘中断和sys.exit吗?很可能不是!所有用户定义的异常都应该继承自“Exception”。“查看异常类层次结构。https://dotnettutorials.net/wp-content/uploads/2020/07/word-image-181.png - John Henckel
这对我完美地起作用了,它捕获了您在使用类似全局类的其他位置可能实现的所有错误。对于本地实现来说,它不是完美的解决方案,但对于生产模式系统来说,它很好。 - Alex

119

你可以使用这种方法来处理一般性异常

try:
    a = 2/0
except Exception as e:
    print e.__doc__
    print e.message

12
这可能无法捕获所有异常,因为所有异常的基类是BaseException,我遇到了一些不属于Exception类族的生产代码。有关详细信息,请参见https://docs.python.org/3/library/exceptions.html?highlight=BaseException。 - DDay
5
这并不能捕获所有的异常。 - Andy_A̷n̷d̷y̷
13
从DDay链接的文档中可以看到:"BaseException异常是所有内置异常的基类,不应该被用户自定义类直接继承(如果需要自定义异常类,请使用Exception)。" 从技术上讲,它应该捕获所有非系统退出的异常。除非您在使用会忽略此规则的代码,或者需要捕获系统退出的异常,否则上述内容应该是可以使用的。 - Peter Cassetta
3
并非所有的异常都会有message属性。 - nullable
2
因为在Python 3中,print是一个函数而不是语句。因此,您需要使用()调用它。例如:print(e.message) - vwvolodya
显示剩余3条评论

64
非常简单的例子,类似于在这里找到的例子:

http://docs.python.org/tutorial/errors.html#defining-clean-up-actions

如果你想捕获所有异常,那么将所有代码放在"try:"语句中,替换掉'print "Performing an action which may throw an exception."'。
try:
    print "Performing an action which may throw an exception."
except Exception, error:
    print "An exception was thrown!"
    print str(error)
else:
    print "Everything looks great!"
finally:
    print "Finally is called directly after executing the try statement whether an exception is thrown or not."

在上面的示例中,您将按以下顺序看到输出:
1)执行可能引发异常的操作。
2)无论是否抛出异常,try语句执行后直接调用finally语句。
3)根据是否抛出异常,输出"An exception was thrown!" 或 "Everything looks great!"。
希望这有所帮助!

我有我的工作从一个HPC管理器中运行。我想捕获(slurm、qsub、condor)管理器何时退出(用我的自定义电子邮件给自己发电子邮件)。except:可以捕获吗?但它不会给我e的句柄 :( - Charlie Parker
2
除了异常(Exception)之外,错误(error):^语法错误(SyntaxError):无效的语法 - Tony
2
@Tony 尝试:except Exception as error: -- 如果你正在运行Python3。 - Joshua Burns

41

使用Python 3.0及以上版本,有多种方法可以实现这一点。

方法1

这是一种简单的方法,但不推荐使用,因为您无法确定到底是哪一行代码引发了异常:

def bad_method():
    try:
        sqrt = 0**-1
    except Exception as e:
        print(e)

bad_method()

方法二

建议采用此方法,因为它提供了有关每个异常更多的详细信息。 它包括:

  • 你的代码行号
  • 文件名
  • 实际错误以更详细的方式呈现

唯一的缺点是需要导入traceback。

import traceback

def bad_method():
    try:
        sqrt = 0**-1
    except Exception:
        print(traceback.print_exc())

bad_method()

我有我的工作从HPC管理器运行。 我想在(slurm、qsub、condor)管理器退出时捕获它(用我的自定义电子邮件向自己发送电子邮件)。 except:会捕获吗? 但它并没有给我一个e的处理方式 :( - Charlie Parker
traceback.print_exc()是否可以连接起来? - Unknow0059

25

我刚刚发现了一个在 Python 2.7 中测试异常名称的小技巧。有时候我在代码中处理特定的异常,所以我需要一个测试来确定该名称是否在已处理的异常列表中。

try:
    raise IndexError #as test error
except Exception as e:
    excepName = type(e).__name__ # returns the name of the exception

我有我的工作从HPC管理器运行。我想捕获(slurm、qsub、condor)管理器何时退出(通过我的自定义电子邮件向自己发送电子邮件)。except:会捕获到吗?但是它没有给我e的句柄:( - Charlie Parker
说实话,我不熟悉HPC...如果它与Python集成,应该有相应的异常实现。如果没有,您可以尝试使用第三方库(不知道哪个)或创建一个任务监听器,该监听器将搜索由HPC设置的标志。如果所有这些都失败了,您可以尝试编写一些自己的代码,“异常”类是可继承的,并深入进程/驱动程序。除此之外,由于缺乏更多信息和SO的无意见政策,我建议在标题和标签中询问新问题,如果以前没有提出过关于HPC的问题。加油 :) - Danilo

22

我正在添加一个额外的方法,它可以捕获完整的异常轨迹信息,这可以帮助您更好地理解错误。

Python 3

import traceback

try:
    # your code goes here
except Exception as e:
    print(e)
    traceback.print_exc()

1
这就是它。 - Joshua Pinter

10
try:
    whatever()
except:
    # this will catch any exception or error

值得一提的是,这并不是正确的Python代码。这将捕获许多您可能不想捕获的错误。

请使用except,而不是像其他答案中提到的那样捕获所有异常。为此,您必须使用BaseException,但正如您所说,没有人应该这样捕获所有异常。我想,如果目标是在开发过程中添加更细粒度的except,那么这样做还可以接受,但我不认为它会持续下去... - Pyglouthon

7

首先,有些异常情况是希望它们打破你的代码(因为当这种错误发生时,你的代码无法正常运行!),而有些异常情况则需要你平稳地捕获。尝试区分它们。你可能不想捕获所有的异常!

其次,你可以花时间去查看你的进程日志,而不是捕获所有异常。假设你正在遇到不同/第三方异常,比如来自像GCP这样的云服务提供商。在日志中,你可以找到你遇到的异常。然后,你可以做类似这样的事情:

from google.api_core.exceptions import ServiceUnavailable, RetryError

for i in range(10):
   try:
      print("do something")

   except ValueError:
      print("I know this might happen for now at times! skipping this and continuing with my loop"

   except ServiceUnavailable:
      print("our connection to a service (e.g. logging) of gcp has failed")
      print("initializing the cloud logger again and try continuing ...") 

   except RetryError:
      print("gcp connection retry failed. breaking the loop. try again later!)
      break

对于其余可能发生或不发生的错误,我会留出空间,以便在遇到意外异常时让我的代码崩溃!这样,我就可以了解正在发生的情况,并通过捕获边缘情况来改进我的代码。

如果您希望由于某种原因永远不崩溃,例如它是嵌入到远程硬件中的代码,您无法轻松访问,那么您可以在结尾处添加一个通用异常处理程序:

except Exception as e:
   print(f"something went wrong! - {e}")

您还可以查看Python 3异常的继承结构 此处。 Exception 和 BaseException 的区别在于,Exception 不会捕获SystemExit、KeyboardInterrupt或GeneratorExit。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接