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

e-s*_*tis 40 python exception with-statement

我知道该with声明可以帮助你解决这个问题:

try:
    f = open(my_file)
    do_stuff_that_fails()
except:
    pass
finally:
    f.close()
Run Code Online (Sandbox Code Playgroud)

成:

with open(my_file) as f:
    do_stuff_that_fails()
Run Code Online (Sandbox Code Playgroud)

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

try:
    with open(my_file) as f:
        do_stuff_that_fails()
except (IOError, OSError, Failure) as e:
    do_stuff_when_it_doesnt_work()
Run Code Online (Sandbox Code Playgroud)

这相当于:

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

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

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

Tim*_*ney 34

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

你已经结构化了,如果尝试打开该文件,那么你将永远不会绑定一个打开的文件的名称抛出一个异常的方式f,导致无论是NameError在在finally条款(如果f从未范围内结合型)或完全的东西意外(如果有).

正确的结构(相当于with)是:

f = open(my_file)

try:
    do_stuff_that_fails()
finally:
    f.close()
Run Code Online (Sandbox Code Playgroud)

(注意 - 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()
Run Code Online (Sandbox Code Playgroud)

第二个是(如另一个答案所述),你不能忘记打电话f.close().

顺便说一句,这个术语是"上下文管理",而不是"资源管理" - with语句管理上下文,其中一些可能是资源,而另一些则不是.例如,它还用于decimal为特定代码块建立小数上下文.

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

  • +1我什至没有注意到我犯了一个如此愚蠢的错误。感谢您指出了这一点。 (2认同)

Mat*_*ner 16

在你给出的例子中,它并不是更好.最好的做法是将异常捕获到接近它们抛出的点,以避免捕获相同类型的无关异常.

try:
    file = open(...)
except OpenErrors...:
    # handle open exceptions
else:
    try:
        # do stuff with file
    finally:
        file.close()
Run Code Online (Sandbox Code Playgroud)

不幸的是,这样做很简单,该with语句不允许您捕获在评估期间抛出的异常.有人建议在邮件列表中添加异常处理:

with open(...) as file:
    # do stuff with file
except OpenErrors...:
    # handle open exceptions
Run Code Online (Sandbox Code Playgroud)

但这被击落了.

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

file = open(...).__enter__()
file.__exit__(typ, val, tb)
Run Code Online (Sandbox Code Playgroud)

这里这里更详细地描述了这一点.

作为一般准则,with对于不期望例外的情况,语句表示优异,并且默认的"输入/打开/获取"行为就足够了.示例包括必需文件和简单锁定.


小智 7

这是资源管理 ......不是你如何对异常做出反应:)

f.close()使用时无法"忘记" with.以这种方式,它起到与usingC#相同的作用.

快乐的编码.