在Python中获取异常详情

33

我必须在同一个循环内打开并写入约10个不同的文件。 例如:

for i in range(0,10):
    try:
        a=5
        file1 = open("file1.txt",'w+')
        file2 = open("file2.txt",'w+')
        #... etc

        print(str(a),file=file1)
        print(str(a)+"hi",file=file2)
        # ... etc
    except: 
        #error handling

我想做的是能够从一般的异常信息中获取特定的异常信息,比如正在被打开或写入的文件是什么。根据我目前的理解,为了实现我的要求,我需要执行类似于以下内容的操作:

for i in range(0,5):
    a=5
    try:
        file1 = open("file1.txt",'w+')
        print(str(a),file=file1)
    except: 
        #error handling for file1
    try:
        file2 = open("file2.txt",'w+')
        print(str(a)+"hi",file=file2)
    except: 
        #error handling for file2

当我需要对大约10个不同文件进行操作时,使用类似第一个例子中的try/except语句会变得非常笨重和不可读。有没有办法从一般的异常信息中获取(例如)文件名信息?基本上,异常可以报告诸如“写入file1时出错”的内容,而不需要针对file1操作使用特定的try/except语句。

编辑:这是对写入文件的数据进行了极度简化。str(a)和str(a)+"hi"并不是实际写入的数据很好的表示;file1可能需要一个硬编码整数,而file2可能需要一个格式化多个变量的字符串。将打开/写入过程概括为一段循环无法很好地工作。

4个回答

52
你可以使用 sys.exc_info 来获取当前正在处理的异常信息,包括异常对象本身。一个 IOError 异常包含了你所需要的所有信息,包括文件名、错误码和描述错误的字符串。
import sys

try:
    f1 = open('example1')
    f2 = open('example2')
except IOError:
    type, value, traceback = sys.exc_info()
    print('Error opening %s: %s' % (value.filename, value.strerror))

try块中的执行显然仍然会在第一个异常后停止。


1
不支持Python 2? - ghchoi
1
@GyuHyeonChoi 你看了吗? - Cairnarvon
1
您没有提到版本。 - ghchoi

28

使用traceback模块:

traceback.print_exc()

(Pdb) tb.print_exc() *** AttributeError: 'traceback' object has no attribute 'print_exc' 这是我在使用Python 3.7.3时得到的结果。 - PeterHeuz
@PeterHeuz,这不是文档所说的:https://docs.python.org/3.7/library/traceback.html#traceback.print_exc - rstackhouse
这是源代码链接:https://github.com/python/cpython/blob/3.7/Lib/traceback.py#L161。 - rstackhouse
1
你知道这是模块函数,而不是实例成员函数,对吧? - rstackhouse
那么,导入tb或类似的东西? - user13566917

3

当使用exc_type, value, exc_traceback = sys.exc_info()时,需要注意可以通过以下方式获取生成异常的文件名:

exc_traceback.tb_frame.f_locals.get('filename')

3

你提到使用循环,但实际上并没有使用循环。请使用循环,这样可以在一个 try 块中写入每个文件的内容。由于似乎你只需要向每个文件写入一个值,所以不需要一直保持它们处于打开状态。

for filename in ['file1.txt', 'file2.txt', ...]:
    try:
        with open(filename, 'w+') as f:
            f.write(str(a)+"whatever")
    except IOError:
        print("Error occurred with", filename)

编辑:如果要向不同的文件写入截然不同的内容,可以事先创建一个字典或其他数据结构,将文件和数据之间的映射存储起来,然后在循环中使用它。

data = {'file1.txt': str(a), 'file2.txt': 'something else', 'file3.txt': str(a)+str(b)}

for filename, output in data.items():
    try:
        with open(filename, 'w+') as f:
            f.write(output)
    except IOError:
        print("Error occurred with", filename)

我本意是让包含的代码被视为已经在循环中了。我会修复这个问题。但无论如何,这并不能真正解决我的问题。写入这些文件的内容可能完全不同。file1可能需要打印一个硬编码的“1”,file2可能需要打印一个变量,而file3可能需要多个变量。最终会在“with”语句中出现一个巨大的if语句;例如,“如果文件名=file1,则写入此内容;如果文件名=file2,则写入那个内容;如果文件名=file3,则写入另一种内容”。代码仍然会很笨重。 - user891876
1
@user891876:然后,在预计算中,在这种情况下将空字符串作为file3的值。或者您可以在循环中包含一个简单的检查,以获取“不写入”值,例如None,并将None放入预先计算的值中。重点是,在打开任何文件之前,您可以决定要写入每个文件的内容。 - BrenBarn
2
更普遍地说,处理每个文件要做出决策的逻辑越复杂,就越难避免“笨重”的代码。如果你想对10个文件进行完全不同的复杂操作,那么这不是一个操作,而是10个操作,你不能期望能够一次性完成它们。尽可能在打开文件之前先处理笨重部分,因为这将有助于将创建数据的逻辑错误(这在您的控制之下)与写入数据时的I/O错误(这可能不在您的控制之下)分开。 - BrenBarn
说实话,按照你建议的方式,我需要做大量的预计算才能完成它,最终代码行数会比我在每个写入文件的行上使用try/except的情况下要多得多(而且更丑陋)。编辑:“将”是一个不好的措辞选择。我实际上已经尝试过了,结果比我开始时还要糟糕。 - user891876
赞同 BrenBarn 的评论。如果每个文件都有自己的逻辑,那么它们需要各自的函数。你无法避免这个。你能想到的最好的方法是一个单独的函数,带有你的 try/except 用于打开文件,但仅此而已。 - bobrobbob
显示剩余2条评论

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