通常我使用with语句在Python中处理文件,就像在这个块中通过HTTP下载资源一样:
with (open(filename), "wb"):
for chunk in request.iter_content(chunk_size=1024):
if chunk:
file.write(chunk)
file.flush()
但这假设我知道文件名。假设我想使用tempfile.mkstemp()
。此函数返回打开文件的句柄和路径名,因此在open
语句中使用with
将是错误的。
我搜索了一下,发现了许多关于小心谨慎使用mkstemp
的警告。几篇博客文章在他们说不丢弃mkstemp
返回的整数时几乎大喊大叫。有关os级文件句柄与Python级文件对象不同的讨论。这没关系,但我找不到能确保
mkstemp
来获取要写入的文件with(open...
模式可以获得的行为。所以我的问题是,在Python中有一种很好的方法来创建和写入mkstemp
生成的文件,可能使用不同类型的状态,或者我必须手动执行{{1或者fdopen
等等。似乎应该有一个明确的模式。
答案 0 :(得分:11)
最简单的编码模式为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库都可以在数据到达时提供数据,许多下载库都可以逐位传输数据 - 正如您可能想象的那样对于cElementTree的XMLParser
和a few different ways中的requests
,情况也是如此。例如:
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()