如何“with”比try / catch更好地在Python中打开文件?

时间:2012-01-08 02:05:29

标签: python exception with-statement

我知道with语句可以帮助你解决这个问题:

try:
    f = open(my_file)
    do_stuff_that_fails()
except:
    pass
finally:
    f.close()

分为:

with open(my_file) as f:
    do_stuff_that_fails()

但那怎么样更好?您仍然需要处理无法打开文件的情况(比如提示用户告诉他他没有权限),所以实际上你有:

try:
    with open(my_file) as f:
        do_stuff_that_fails()
except (IOError, OSError, Failure) as e:
    do_stuff_when_it_doesnt_work()

相当于:

try:
    f = open(my_file)
    do_stuff_that_fails()
except (IOError, OSError, Faillure) as e:
    do_stuff_when_it_doesnt_work()
finally:
    f.close()

是的,你获得了两行,但是你添加了一个嵌套级别,这使得它更容易阅读。 with语句的目的是为了节省两行还是我遗漏了什么?

为此添加一个关键字似乎很多,所以我觉得有一些语法来处理额外的尝试/除了我不知道的。

3 个答案:

答案 0 :(得分:34)

首先,它有助于防止您在try ... finally ...示例中引入的问题。

您构建它的方式,如果在尝试打开文件时抛出异常,那么您永远不会将打开的文件绑定到名称f,从而导致NameError中的finally f子句(如果with从未在范围内绑定)或完全意外的事情(如果有)。

正确的结构(相当于f = open(my_file) try: do_stuff_that_fails() finally: f.close() )是:

except

(注意 - 如果你无事可做的话,不需要try: f = open(my_file) try: do_stuff_that_fails() except EXPECTED_EXCEPTION_TYPES as e: do_stuff_when_it_doesnt_work() finally: f.close() except (IOError, OSError) as e: do_other_stuff_when_it_we_have_file_IO_problems() 条款。

你的第二个例子同样是错误的,应该结构如下:

f.close()

第二个是(如另一个答案所述)你不能忘记致电with

BTW,术语是“上下文管理”,而不是“资源管理” - decimal语句管理上下文,其中一些可能是资源,但有些则不是。例如,它还与os.walk一起用于为特定代码块建立小数上下文。

最后(回复你对前一个答案的评论)你不应该依赖refcount语义来处理Python中的资源。 Jython,IronPython和PyPy都有非引用语义,并且没有什么可以防止CPython以其他方式运行(尽管在不久的将来它不太可能)。在紧密循环(例如{{1}})中,如果在具有不同行为的VM上运行依赖于refcount语义的代码,则很容易用完文件句柄。

答案 1 :(得分:16)

在您给出的示例中,更好。最好的做法是尽可能地捕获异常,以避免捕获相同类型的无关异常。

try:
    file = open(...)
except OpenErrors...:
    # handle open exceptions
else:
    try:
        # do stuff with file
    finally:
        file.close()

由于不幸的是,with statement不允许您捕获在评估期间抛出的异常。在邮件列表中有一个suggestion来为此效果添加异常处理:

with open(...) as file:
    # do stuff with file
except OpenErrors...:
    # handle open exceptions

但这是shot down

最后值得注意的是,您可以直接进入和退出上下文管理器,如下所示:

file = open(...).__enter__()
file.__exit__(typ, val, tb)

更详细地介绍了herehere

作为一般准则,with语句适用于不期望例外的情况,默认的“输入/打开/获取”行为就足够了。示例包括必需文件和简单锁定。

答案 2 :(得分:7)

用于资源管理 ...不是因为您对异常的反应,否则:)

没有办法忘记"使用f.close()with。通过这种方式,它在C#中扮演与using相同的角色。

快乐的编码。