我应该在urllib.urlopen()后调用close()吗?

76

我是 Python 的新手,正在阅读别人的代码:

urllib.urlopen() 后面是否应该跟着 urllib.close()?否则会造成连接泄漏,对吗?

5个回答

109

close方法必须在urllib.urlopen结果对象上调用,而不是在你想象中的urllib模块本身上进行调用(正如你提到的urllib.close - 这并不存在)。

最好的做法是:不要使用x = urllib.urlopen(u)等方式,而要使用:

import contextlib

with contextlib.closing(urllib.urlopen(u)) as x:
   ...use x at will here...

with语句和closing上下文管理器可以确保在异常情况下也能正确关闭。


11
像这样做怎么样:data = urllib2.urlopen('url').read() - Facundo Casco
21
在Python 3中,直接支持with语句。以下是示例代码:with urllib.urlopen(u) as x: ... - merwok
为什么Python3文档在这个(咳咳)上下文中仍然提到contextlib.closing - user66081
@ÉricAraujo:在Python 3中,urllib.urlopen根本不存在。 - Eric
它已经移动到新的子模块urllib.request中:https://docs.python.org/3/library/urllib.request.html#urllib.request.urlopen - merwok

13

就像 @Peter 所说的那样,超出范围的打开的URL将会成为垃圾回收的候选对象。

但是,还要注意在CPython中URLopener定义了 :

 def __del__(self):
     self.close()

这意味着当该实例的引用计数归零时,它的 __del__ 方法将被调用,从而也会调用其 close 方法。最常见的使引用计数归零的方式是让实例超出作用域,但没有什么严格的限制阻止你提前显式地使用 del x(但直接减少一次引用计数,而不是直接调用 __del__)。

显式关闭资源通常是一个好的做法,特别是当应用程序可能使用过多的资源时,但如果你不保留对不再需要的实例的(循环?)引用等诡异操作,Python 也会自动为您进行清理。


3
然而,可能会超越垃圾回收器的处理能力 -- 我曾经遇到这种情况,创建的文件句柄比关闭它们的速度更快 [但是显式调用 gc.collect() 或者 close() 可以清理掉这些句柄]。 - Charles Duffy

6

严格来说,这是正确的。但实际上,一旦(如果)urllib超出范围,连接将被自动垃圾回收器关闭。


12
这在某些 Python 实现中是正确的,但是 Python 语言并不保证对象离开作用域时会立即关闭。请参考 Jython。 - John La Rooy
1
@gnibbler 这个回答的作者并没有说这会很快发生,只是说它会发生。 - Piotr Dobrogost
3
@Piotr,但如果我使用循环打开网址并且垃圾回收器没有足够快地清空它们,那么程序可能会崩溃。这是一种相当粗糙的做法,不适合生产代码。 - John La Rooy
2
Python中的no-op GC(即从不运行的GC)是完全有效的。您无法保证GC将永远不会运行。而且,在大多数Python实现中,gc.disable可以禁用GC。 - gsnedders
1
我在 GC 进行任何清理之前,成功耗尽了可用连接。因此,如果您不想突然失去连接,那么是的,您应该调用 close。 - andrew pate

1
你在使用 IronPython 时,基本上需要显式关闭连接。自动关闭取决于垃圾回收机制。我遇到了一个情况,垃圾回收运行时间太长,导致Windows的套接字不足。我以高频率(即IronPython和连接允许的最高频率,约为7Hz)轮询Web服务器。我可以看到PerfMon上的“已建立连接”(即正在使用的套接字)不断增加。解决方案是在每次调用urlopen后调用gc.collect()

0
urllib.request 模块使用 HTTP/1.1 协议,并在其 HTTP 请求中包含 Connection:close 头部。这是官方文档中的内容,您可以在 此处 进行查看。

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