使用和关闭Python中的文件

nek*_*imi 9 python with-statement contextmanager urlopen

我已经读过,这样打开的文件会在离开with块时自动关闭:

with open("x.txt") as f:
    data = f.read()
    do something with data
Run Code Online (Sandbox Code Playgroud)

但是当从网上开放时,我需要这个:

from contextlib import closing
from urllib.request import urlopen

with closing(urlopen('http://www.python.org')) as page:
    for line in page:
        print(line)
Run Code Online (Sandbox Code Playgroud)

为什么和有什么区别?(我使用的是Python3)

aba*_*ert 10

细节有点技术性,所以让我们从简单的版本开始:

某些类型知道如何在with语句中使用.文件对象,就像你从中得到的那样open,就是这种类型的一个例子.事实证明,这你从后面的物体urllib.request.urlopen,都这样类型的一个例子,所以你的第二个例子可以写成方式与第一相同.

但有些类型不知道如何在with声明中使用.该closing函数旨在包装这些类型 - 只要它们有一个close方法,它将close在您退出with语句时调用它们的方法.

当然,某些类型不知道如何在with语句中使用,也不能使用,closing因为它们的清理方法没有命名close(或者因为清理它们比仅关闭它们更复杂).在这种情况下,您需要编写自定义上下文管理器.但即使这通常也不那么难.


在技​​术方面:

一个with语句需要上下文管理器,与对象__enter____exit__方法.它将调用该__enter__方法,并在该as子句中为您提供该方法返回的值,然后它将__exit__with语句结束时调用该方法.

文件对象继承自io.IOBase,其__enter__方法返回自身的上下文管理器及其__exit__调用self.close().

返回的对象urlopen是(假设一个httphttpsURL)一个HTTPResponse,正如文档所说,"可以与with语句一起使用".

closing函数:

返回一个上下文管理器,它在块完成时关闭.这基本上相当于:

@contextmanager
def closing(thing):
    try:
        yield thing
    finally:
        thing.close()
Run Code Online (Sandbox Code Playgroud)

在文档中并不总是100%清楚哪些类型是上下文管理器,哪些类型不是.特别是因为从3.1开始就有了一个主要的驱动因素,将所有可能成为上下文管理器的东西组合成一个(并且,就此而言,IOBase如果有意义的话,将所有大部分文件都变成实际的),但它仍然不是100自3.4起完成%.

你可以随时试试看看.如果你得到一个AttributeError: __exit__,那么该对象不能用作上下文管理器.如果您认为应该是,请提交一个提示更改的错误.如果您没有收到该错误,但文档没有提及它是合法的,请提交一个错误,建议更新文档.


Mar*_*ers 8

你没有.urlopen('http://www.python.org')也返回一个上下文管理器:

with urlopen('http://www.python.org') as page:
Run Code Online (Sandbox Code Playgroud)

这在urllib.request.urlopen()页面上记录:

对于传统URLopenerFancyURLopener类处理的ftp,文件和数据URL以及请求显式,此函数返回一个可用作上下文管理器urllib.response.addinfourl对象[...].

强调我的.对于HTTP响应,返回http.client.HTTPResponse()object,它也是一个上下文管理器:

响应是一个可迭代的对象,可以在with语句中使用.

实施例部分还使用对象作为上下文管理器:

由于python.org网站使用其元标记中指定的utf-8编码,我们将使用相同的方法来解码字节对象.

>>> with urllib.request.urlopen('http://www.python.org/') as f:
...     print(f.read(100).decode('utf-8'))
...
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtm
Run Code Online (Sandbox Code Playgroud)

返回的对象open()也是上下文管理器 ; 他们实施特殊的方法object.__enter__()object.__exit__().

contextlib.closing()文档使用的示例urlopen()已过时; 在Python 2中,前身urllib.request.urlopen()没有生成上下文管理器,您需要使用该工具自动关闭与上下文管理器的连接.这已通过问题541812365修复,但该示例未更新.我创建了问题22755,要求提供一个不同的例子.