这里最简单的解决方案是自己将文件读入内存,然后传递给请求。请注意,正如文档所述,“如果您想,可以发送字符串以作为文件接收”。因此,请这样做。
换句话说,不要像构建这样的字典:
files = {tag: open(pathname, 'rb') for (tag, pathname) in stuff_to_send}
建立它的方式如下:
def read_file(pathname):
with open(pathname, 'rb') as f:
return f.read()
files = {tag: read_file(pathname) for (tag, pathname) in stuff_to_send}
现在您一次只能打开一个文件,这是有保证的。
这可能看起来很浪费,但实际上并不是——如果您不这样做,requests
将会从所有文件中读取所有数据。*
但与此同时,让我回答您的实际问题,而不仅仅告诉您应该怎么做。
由于我没有句柄来关闭这些打开的文件,我们看到了这个错误。
当然您有。您有一个字典,其值为这些打开的文件。
事实上,如果您没有对它们进行处理,这个问题可能会更少发生,因为垃圾收集器会(通常情况下,但不一定稳健可靠,不能指望)为您处理这些问题。事实上它从未这样做过,这意味着您必须对它们进行处理。
有没有办法以块的方式关闭这些文件?
当然可以。我不知道您如何分块,但假设每个块都是一组键,您正在传递 files = {key: files[key] for key in chunk}
,对吧?
因此,在请求之后,请执行以下操作:
for key in chunk:
files[key].close()
或者,如果您正在像这样为每个块构建一个
dict
:
files = {tag: open(filename, 'rb') for (tag, filename) in chunk}
只需要这样做:
for file in files.values():
file.close()
requests模块会自动关闭打开的文件吗?
不会。你需要手动关闭。
在许多情况下,由于请求后files
变量很快就会消失,而一旦没有任何对该字典的引用,则其会很快被清除(使用CPython并且没有环路时立即清除;如果其中一个不是真的,则只是“很快”),这意味着所有文件都会很快被清理,此时析构函数会自动关闭它们。但是您不能依赖这一点,请始终明确地关闭文件。
files.clear()
之所以有效是因为它与让files
消失做了相同的事情:它强制字典忘记所有文件,从而删除了每个文件的最后一个引用,这意味着它们将很快被清除等等。
* 如果您没有足够的页面空间来容纳所有内容怎么办?那么您无论如何都无法一次性发送所有内容。您需要进行单独的请求,或使用流式API——我认为这也意味着您必须手动执行多部分操作。但是,如果您有足够的页面空间,只是没有足够的真实RAM,因此尝试读取所有内容会将您带入交换困境地狱,则可以通过将它们全部连接在磁盘上,打开巨大的文件,mmap
映射其段,并将其作为字符串发送来解决这个问题...
files.clear()
来关闭所有打开的文件,使用for f in files.values(): f.close()
会更好。 - mgilsonf.close()
自己关闭它们。 - randomusernameulimit -n
应该可以确认您已经知道的事情——对于您来说,限制为256。 - mgilson