使用Python中的“with”语句和try-except块

122

在使用Python的"with"语句与try-except语句块结合时,这是正确的方式吗?

try:
    with open("file", "r") as f:
        line = f.readline()
except IOError:
    <whatever>

如果是这样,那么考虑旧的做法:

try:
    f = open("file", "r")
    line = f.readline()
except IOError:
    <whatever>
finally:
    f.close()

使用“with”语句的主要好处在于我们可以省去三行代码吗? 对于此用例,它似乎并不那么令人信服(尽管我理解“with”语句有其他用途)。

编辑:上述两个代码块的功能是否相同?

编辑2:前几个答案通常谈论使用“with”的好处,但这些好处在这里看起来并不明显。 多年来,我们一直在明确调用f.close(),或者至少应该这样做。 我想一个好处是懒散的程序员将受益于使用“with”语句。


可能是Catching an exception while using a Python 'with' statement的重复问题。 - Filippo Mazza
对我来说,不必记得在finally语句中关闭()东西就足以使用'with'。我见过很多代码没有关闭它的资源。而且据我所知,'with'没有缺点。 - Raúl Salinas-Monteagudo
4个回答

157
  1. 你提供的两个代码块并不相等
  2. 你所描述的旧式做法存在一个严重的错误:如果打开文件失败,你将会在finally语句块中再次触发异常,因为f未被绑定。

等效的旧式代码如下:

try:
    f = open("file", "r")
    try:
        line = f.readline()
    finally:
        f.close()
except IOError:
    <whatever>

正如你所看到的,with语句可以使事情变得更少出错。在较新版本的Python(2.7,3.1)中,你也可以在一个 with 语句中组合多个表达式。例如:

with open("input", "r") as inp, open("output", "w") as out:
    out.write(inp.read())
此外,我个人认为尽早捕获任何异常是一种不好的习惯。这不是异常的目的。如果可能失败的IO函数是更复杂操作的一部分,在大多数情况下,IOError应该中止整个操作并在外部级别处理。使用with语句,您可以摆脱所有这些内部级别的try...finally语句。

3
早期使用异常是可以的。Python鼓励在很多情况下使用异常来进行控制。 - JeffCharter
也许这是个坏习惯,但我总是想要“尝试”特定的事情,然后知道我捕捉到了我想要捕捉的东西而不是其他东西。主要是为了能够编写更好的错误消息吧。所以我觉得不能只“尝试”打开文件很令人沮丧。如果我同时打开其他东西会怎么样呢?如果我把所有东西都包在一个大的try/except IOError中,那么我就开始对任何IOErrors的原因感到困惑。或者'except'语句离'try'太远了,它的目的变得不清楚。 - Ben Farmer
或者说我正在一次打开多个文件,如果其中一个文件存在而另一个文件不存在,我想要能够执行某些操作。我猜只能回到事先明确检查哪些文件存在的方式了? - Ben Farmer

8
如果finally块的内容取决于被打开的文件对象的属性,那么为什么不让文件对象的实现者编写finally块呢?这就是with语句的好处,远不止在这种情况下节省三行代码那么简单。
是的,你将with和try-except组合使用的方式几乎是唯一的方法,因为在open语句本身引起的异常错误无法在with块内捕获。

0

我认为你对于“with”语句的理解有误,它不仅仅是减少代码行数,而且还可以进行初始化和处理拆卸。

在你的情况下,“with”会:

  • 打开一个文件,
  • 处理其内容,并且
  • 确保关闭它。

这里有一个了解“with”语句的链接:http://effbot.org/zone/python-with-statement.htm

编辑:是的,你使用“with”的方式是正确的,两个代码块的功能是相同的。关于为什么要使用“with”?因为它带来的好处。就像你提到的意外遗漏f.close()。


-7
以下代码的更Pythonic的写法为:
try:
    f = open("file", "r")
    try:
        line = f.readline()
    finally:
        f.close()
except IOError:
    <whatever>

try:
    f = open("file", "r")
except IOError:
    <whatever>
else:
    f.close()

1
我已经为您添加了代码格式,这使得它更容易阅读。但是您可能需要仔细检查以确保我没有破坏缩进。 - andrewsi
3
不,你的版本与原始代码的功能不同。即使添加了缺失的 readline() 调用,如果 readline() 导致 IOError,你的版本也不会关闭文件。 - Aleksi Torhamo

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