2

我目前正在尝试编写代码来捕获异常,并且根据引发的异常,将导入与未引发异常时不同的模块。

try:
  import sge_execution_engine as execution_engine
except ImportError: 
  print "Use local execution engine because SGE support is missing!"
  print sys.exc_info() # Print out the exception message
  import local_execution_engine as execution_engine
except RuntimeError:
  print "Using local execution engine because SGE support is missing!"
  print sys.exc_info()
  import local_execution_engine as execution_engine

第一个异常,ImportError即被捕获,捕获drmaa在执行过程中找不到python模块时抛出的异常import sge_execution_engine(里面sge_execution_engine有一条import drmaa语句)。第二个异常, ,在找到 python 库RuntimeError时被捕获(就像在执行中的语句期间一样),但C 库没有安装到操作系统中。我们希望这两个语句足以捕获当用户尝试在没有 python库、C 库或未安装 Sun Grid Engine 的机器上运行此模块时可能引发的所有可能的异常。没有任何这些收益,模块将继续drmaaimport drmaasge_execution_enginedrmaaexceptdrmaadrmaaimport local_execution_engine这样代码就可以在本地用户的机器上执行。现在,代码按预期工作,因为它在使用 sge 发现异常时会导入本地,但我们仍在寻求改进此处的异常处理以使其更加健壮。

在我看来,我认为将抛出的实际异常消息打印到标准输出是很好的,因为它可以让用户知道为什么他无法导入 sge_execution_engine 尤其是如果他不希望导入失败。

然而,我意识到,也许更好的方法是使用格式,然后打印出来,并调用与抛出和分配的异常相关的一些属性,而不是使用print sys.exc_info()实际在屏幕上打印异常消息到.except EXCEPTION as some_variable_nameprint some_variable_namesome_variable_name

我在Python 教程中看到这是在有这段代码的异常中完成的:

import sys

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

似乎该块通过专门调用对象的and属性以except IOError as e细粒度的方式处理异常消息。但是,当我查看文档时,我没有看到这些特定属性被列为异常文档的一部分。事实上,Python 文档中的所有其他异常也是如此,因此我们似乎无法确定哪些属性将与特定异常相关联。如果我们对此一无所知,那么当我们使用语法来处理我们的异常时,我们如何能够弄清楚要在对象上调用哪些属性呢?errnostrerrorIOErrorIOError some_variable_nameimport EXCEPTION as some_variable_name

我将不胜感激任何人对此的建议,即使您的回答没有直接回答我的问题,但是如果您对如何更好地处理我的异常有另一个完全不同的建议,请不要犹豫,发帖!

非常感谢!

4

2 回答 2

0

首先,您是对的,最好将异常捕获到变量中,而不是忽略它,然后用sys.exc_info(). 有几个很好的理由使用exc_info(低级代码、必须同时使用 2.6 和 3.x 之前的代码等),但一般来说,当你可以按照自己的方式去做时,你应该这样做。

这甚至适用于你最后一次裸露的除外。在 2.7 中,plainexcept:与 的含义相同except Exception:,因此您可以编写except Exception as e:,然后使用该e值。

另请注意,如果您想对多种异常类型执行完全相同的操作,您可以编写except (RuntimeError, ImportError) as e:.

至于“让它更健壮”,这不是绝对的事情。例如,如果有一些既不是 aRuntimeError也不是 an的意外异常,ImportError您是要记录它并尝试回退代码,还是完全转储回溯?例如,如果它是SyntaxError由某人签入对您的程序的错误编辑引起的,您可能不想将其视为运行时错误……或者您可能会这样做;这实际上取决于您的开发实践和目标用户群。


同时:

似乎该块通过专门调用对象的and属性以except IOError as e细粒度的方式处理异常消息。但是,当我查看文档时,我没有看到这些特定属性被列为异常文档的一部分。errnostrerrorIOErrorIOError

您需要查找层次结构。请注意,它IOError是 的子类EnvironmentError,它记录了errnostrerror属性。(这些属性的实际含义仅为文档OSError及其子类,但它们存在的事实已记录在案。)

如果您认为这有点混乱……好吧,确实如此。这一切都在 Python 3.x 中进行了清理,其中IOErrorEnvironmentError被合并到OSError中,清楚地记录了它的属性,并且您通常不必errno首先打开,因为公共errno值会生成特定的子类FileNotFoundError,等等。但是,只要您使用的是 2.7,您就不会从过去 6 年的语言改进中受益。


例如,查看层次结构或ValueError=>StandardError=>Exception(从层次结构中的最低到最高),我找不到任何关于它的属性。

如果你dira ValueError,你会发现它只有两个属性(除了通常的特殊东西,比如__repr__and __class_):argsmessage.

message没有记录,因为它在 2.5 中已被弃用,并且仅存在于 2.7 中以允许某些 2.5 之前的代码继续运行。* 但args已记录;您只需要再上一层,即可BaseException

args

给异常构造函数的参数元组。一些内置异常(如IOError)需要一定数量的参数并为此元组的元素分配特殊含义,而另一些通常仅使用单个字符串调用并给出错误消息。

所以,你找不到其他属性的原因ValueError是没有其他属性可以找到。其他课程也是如此。少数具有特殊属性的类型(OSError, SyntaxError,可能是标准库中其他地方的一些特定于模块的类型)明确记录它们。**

如果我们使用except some_exception as e语法,则print e足以在不调用其属性的情况下打印出异常

打印一些有用的异常形式就足够了。同样,来自BaseException文档:

如果在此类的实例上调用str()or unicode(),则返回实例的参数表示形式,或者在没有参数时返回空字符串。

在某些情况下,这不是您想要的。特别要注意它不包括异常的类型。你可以用 得到它repr,它给你一些看起来像构造函数调用的东西(例如,ValueError("invalid literal for int() with base 10: 'f'"))。

如果您想要在回溯中获得相同的输出,您必须自己将typeand the strorunicode放在一起 - 例如,'{}: {}'.format(type(e), e).

如果你想从异常中获取实际信息,比如那个基数10或那个字符串'f'——好吧,你不能,***因为这些信息已经被丢弃了。您必须编写自己的代码来跟踪它,例如:

try:
    i = int(s, 16)
except ValueError as e:
    print '{} is not a base-16 number'.format(s)

* 就好像BaseException.__init__(self, *args)被定义为self.args = tuple(args); self.message = str(args[0]) if args else ''.

** 我相信在 2.5 中,有一些异常类型具有未记录的属性作为 CPython 的实现细节,但到 2.7 时,它们要么全部消失,要么记录在案。

*** 好吧,您可以解析异常字符串,但不用说,这是一个实现细节,不能保证稳定和可移植。它在 Jython 或西班牙语系统中可能有所不同,或者它可能不会按照您期望的方式引用字符串等。

于 2014-11-25T02:59:47.303 回答
0

使用裸机except很少是一个好主意,但这是很少见的情况之一 - 主要是因为您有一个要使用的备份,如果由于任何原因您无法导入系统sge

try:
    import sge_execution_engine as execution_engine
except:
    print "Use local execution engine because SGE support is missing!"
    print sys.exc_info() # Print out the exception message
    import local_execution_engine as execution_engine

请注意,您现在只需要一个except子句。

处理向用户获取该信息的更好方法可能是继续打印出来,然后还使用日志记录模块进行永久记录:

import logging
logger = logging.getLogger()
logger.setLevel(logging.WARNING)

然后在您的except子句中添加:

    logger.exception('unable to import sge')

并且您的消息以及实际异常将保存在日志文件中。

于 2014-11-25T03:03:01.157 回答