在Python中使用异常和文件时的清理

3 投票
5 回答
1128 浏览
提问于 2025-04-15 13:00

我学习Python已经几天了,但对它的“精神”有点摸不着头脑。之前我主要用C/C++/Java/Perl,所以我知道Python和C完全不一样,这也是我想理解Python的“精神”,以便能更好地使用它(到目前为止,这确实有点难)...

我特别想了解的是异常处理和清理的部分:这篇文章最后的代码是为了模拟一个比较常见的情况,就是打开和解析文件时,如果出错需要关闭文件...

我看到的大多数示例都使用了try语句的'else'部分来关闭文件... 这让我觉得很合理,直到我意识到错误可能是因为:

  • 打开文件时出错(这种情况下就不需要关闭没有打开的文件)
  • 解析文件时出错(这种情况下需要关闭文件)

这里的陷阱是,如果你在try块中使用'else'部分,那么如果在解析时出错,文件就不会被关闭!另一方面,使用'finally'部分又会导致需要额外的检查,因为如果在打开文件时出错,file_desc变量可能根本不存在(见下面代码中的注释)...

这种额外的检查效率低下,而且很麻烦,因为任何合理的程序可能会包含成百上千个符号,而解析dir()的结果真是让人头疼... 更不用说这样的语句可读性差了...

其他大多数语言允许变量定义,这样可以解决这个问题... 但在Python中,一切似乎都是隐式的...

通常情况下,你只需声明一个file_desc变量,然后为每个任务使用多个try/catch块... 一个用于打开,一个用于解析,最后一个用于关闭... 不需要嵌套它们... 但在这里我不知道怎么声明这个变量... 所以我在问题的开始就卡住了!

那么,Python在这里的“精神”是什么呢???

  • 把打开和解析分成两个不同的方法?怎么做?
  • 使用某种嵌套的try/except块???怎么做?
  • 也许有办法声明file_desc变量,这样就不需要额外的检查了... 这可能吗???值得吗???
  • 那close()语句呢???如果它抛出错误怎么办?

谢谢你的提示... 下面是示例代码:

class FormatError(Exception):
    def __init__(self, message):
        self.strerror = message
    def __str__(self):
        return repr(message)


file_name = raw_input("Input a filename please: ")
try:
    file_desc = open(file_name, 'r')
    # read the file...
    while True:
        current_line = file_desc.readline()
        if not current_line: break
        print current_line.rstrip("\n")
    # lets simulate some parsing error...
    raise FormatError("oops... the file format is wrong...")
except FormatError as format_error:
    print "The file {0} is invalid: {1}".format(file_name, format_error.strerror)
except IOError as io_error:
    print "The file {0} could not be read: {1}".format(file_name, io_error.strerror)
else:
    file_desc.close()
# finally:
#     if 'file_desc' in dir() and not file_desc.closed:
#        file_desc.close()

if 'file_desc' in dir():
    print "The file exists and closed={0}".format(file_desc.closed)
else:
    print "The file has never been defined..."

5 个回答

1

从Python 2.5开始,新增了一个叫做with的命令,它可以让你在编程时更简单地处理一些事情。想了解更多,可以点击这里。下面是你代码的一个改进版本:

class FormatError(Exception):
    def __init__(self, message):
        self.strerror = message
    def __str__(self):
        return repr(message)


file_name = raw_input("Input a filename please: ")
with open(file_name, 'r') as file_desc:
    try:
        # read the file...
        while True:
            current_line = file_desc.readline()
            if not current_line: break
            print current_line.rstrip("\n")
        # lets simulate some parsing error...
        raise FormatError("oops... the file format is wrong...")
    except FormatError as format_error:
        print "The file {0} is invalid: {1}".format(file_name, format_error.strerror)
    except IOError as io_error:
        print "The file {0} could not be read: {1}".format(file_name, io_error.strerror)

if 'file_desc' in dir():
    print "The file exists and closed={0}".format(file_desc.closed)
else:
    print "The file has never been defined..."
3

只是提醒一下:你可以随时声明一个变量,然后它就会变成这样:

file_desc = None
try:
    file_desc = open(file_name, 'r')
except IOError, err:
    pass
finally:
    if file_desc:
        close(file_desc)

当然,如果你使用的是更新版本的Python,使用上下文管理器的方式会更好;不过,我想强调的是,如何在Python中通用地处理异常和变量的作用域。

6

处理这个问题最简单的方法是利用Python 2.5及以上版本中的文件对象是上下文管理器这一特性。你可以使用with语句来进入一个上下文;当你离开这个with的范围时,上下文管理器的__exit__方法会自动被调用。这样,文件对象的上下文管理会自动关闭文件。

try:
    with file("hello.txt") as input_file:
        for line in input_file:
            if "hello" not in line:
                 raise ValueError("Every line must contain 'hello'!")
except IOError:
    print "Damnit, couldn't open the file."
except:
    raise
else:
    print "Everything went fine!"

比如,打开的hello.txt文件句柄会自动关闭,而在with范围内产生的异常会被传递到外面。

撰写回答