最简单的编码模式是try:
/ finally:
:
fd, pathname = tempfile.mkstemp()
try:
dostuff(fd)
finally:
os.close(fd)
但是,如果您不止一次这样做,将其包装在上下文管理器中是微不足道的:
@contextlib.contextmanager
def mkstemping(*args):
fd, pathname = tempfile.mkstemp(*args)
try:
yield fd
finally:
os.close(fd)
然后你可以这样做:
with mkstemping() as fd:
dostuff(fd)
当然,如果你真的想这样做,你总是可以将 fd 包装在一个文件对象中(通过将它传递给open
,或者os.fdopen
在旧版本中)。但是……为什么还要麻烦呢?如果您想要一个 fd,请将其用作 fd。
如果您不想要 fd,除非您有充分的理由需要mkstemp
而不是更简单和更高级别的NamedTemporaryFile
,否则您不应该使用低级 API。只需这样做:
with tempfile.NamedTemporaryFile(delete=False) as f:
dostuff(f)
除了更简单之外with
,它还有一个优点,即它已经是一个 Python 文件对象,而不仅仅是一个 OS 文件描述符(而且,在 Python 3.x 中,它可以是一个 Unicode 文本文件)。
一个更简单的解决方案是完全避免使用临时文件。
几乎所有 XML 解析器都有解析字符串而不是文件的方法。使用cElementTree
,只需调用fromstring
而不是parse
。所以,而不是这个:
req = requests.get(url)
with tempfile.NamedTemporaryFile() as f:
f.write(req.content)
f.seek(0)
tree = ET.parse(f)
......只需这样做:
req = requests.get(url)
tree = ET.fromstring(req.content)
当然,第一个版本只需要在内存中一个接一个地保存 XML 文档和解析树,而第二个需要同时保存它们,因此这可能会使您的峰值内存使用量增加约 30%。但这很少是问题。
如果这是一个问题,许多 XML 库有办法在数据到达时提供数据,许多下载库有办法一点一点地流式传输数据——正如您可能想象的那样,这对于 cElementTreeXMLParser
和requests
in几种不同的方式。例如:
req = requests.get(url, stream=True)
parser = ET.XMLParser()
for chunk in iter(lambda: req.raw.read(8192), ''):
parser.feed(chunk)
tree = parser.close()
不像使用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()