有没有办法自动关闭mkstemp()返回的Python临时文件

Ray*_*oal 2 python mkstemp

通常我使用with语句在Python中处理文件,就像在这个块中通过HTTP下载资源一样:

with (open(filename), "wb"):
    for chunk in request.iter_content(chunk_size=1024):
        if chunk:
            file.write(chunk)
            file.flush()
Run Code Online (Sandbox Code Playgroud)

但这假设我知道文件名.假设我想使用tempfile.mkstemp().此函数返回打开文件和路径名的句柄,因此openwith语句中使用将是错误的.

我搜索了一下,发现了很多关于小心使用的警告mkstemp.一些博客文章几乎喊出来,当他们说不要扔掉返回的整数mkstemp.有关os级文件句柄与Python级文件对象不同的讨论.这没关系,但我找不到最简单的编码模式来确保这一点

  • mkstemp 被调用来获取要写入的文件
  • 写完之后,即使出现异常,Python文件及其底层的os文件句柄也会干净地关闭.这正是我们可以通过with(open...模式获得的行为.

所以我的问题是,在Python中有一种很好的方式来创建和写入mkstemp生成的文件,可能使用不同类型的状态,或者我必须手动执行类似fdopenclose等等.似乎应该有一个明确的这种模式.

aba*_*ert 11

最简单的这种编码模式try:/ finally::

fd, pathname = tempfile.mkstemp()
try:
    dostuff(fd)
finally:
    os.close(fd)
Run Code Online (Sandbox Code Playgroud)

但是,如果您不止一次这样做,将它包装在上下文管理器中是微不足道的:

@contextlib.contextmanager
def mkstemping(*args):
    fd, pathname = tempfile.mkstemp(*args)
    try:
        yield fd
    finally:
        os.close(fd)
Run Code Online (Sandbox Code Playgroud)

然后你可以这样做:

with mkstemping() as fd:
    dostuff(fd)
Run Code Online (Sandbox Code Playgroud)

当然,如果你真的想要,你总是可以将fd包装在一个文件对象中(通过传递给它open,或者os.fdopen在旧版本中).但是......为什么要去额外的麻烦?如果你想要一个fd,请将它用作fd.

如果你想要一个fd,除非你有充分的理由需要mkstemp而不是更简单和更高级别NamedTemporaryFile,你应该使用低级API.这样做:

with tempfile.NamedTemporaryFile(delete=False) as f:
    dostuff(f)
Run Code Online (Sandbox Code Playgroud)

除了更简单之外with,它还具有以下优点:它已经是Python文件对象而不仅仅是OS文件描述符(并且在Python 3.x中,它可以是Unicode文本文件).


更简单的解决方案是完全避免使用tempfile.

几乎所有的XML解析器都有办法解析字符串而不是文件.有了cElementTree,这只是呼唤fromstring而不是parse.所以,而不是这个:

req = requests.get(url)
with tempfile.NamedTemporaryFile() as f:
    f.write(req.content)
    f.seek(0)
    tree = ET.parse(f)
Run Code Online (Sandbox Code Playgroud)

......这样做:

req = requests.get(url)
tree = ET.fromstring(req.content)
Run Code Online (Sandbox Code Playgroud)

当然第一个版本只需要将XML文档和解析后的树一个接一个地保存在内存中,而第二个版本需要同时保存它们,这样可能会使峰值内存使用量增加约30%.但这很少成为问题.

如果它一个问题,很多XML库有办法在数据饲料,因为它到来时,许多下载库有办法通过将数据流比特位,正如你可能想象,这是一次真正的cElementTree的XMLParserrequests几种不同的方式.例如:

req = requests.get(url, stream=True)
parser = ET.XMLParser()
for chunk in iter(lambda: req.raw.read(8192), ''):
    parser.feed(chunk)
tree = parser.close()
Run Code Online (Sandbox Code Playgroud)

不仅仅是使用简单fromstring......但它仍然比使用临时文件更简单,并且可能更有效地启动.

如果使用双参数形式iter会让你感到困惑(很多人一开始很难理解它),你可以将其重写为:

req = requests.get(url, stream=True)
parser = ET.XMLParser()
while True:
    chunk = req.raw.read(8192)
    if not chunk:
        break
    parser.feed(chunk)
tree = parser.close()
Run Code Online (Sandbox Code Playgroud)