7.7. tempfile — 临时文件对象
目的:创建临时文件系统对象。
安全地创建具有唯一名称的临时文件,以至于他们不会被那些想破坏或者窃取数据的人猜出是非常有挑战性的。tempfile
模块提供了几个安全地创建系统临时文件的方法。 TemporaryFile()
打开并返回一个未命名的临时文件, NamedTemporaryFile()
打开并返回一个命名临时文件,SpooledTemporaryFile
会在将数据写入磁盘之前将内容保存在内存中,TemporaryDirectory
是一个上下文管理器,它会在上下文关闭的时候移除目录。
临时文件
需要临时文件存储数据并且不需要和其他程序共享数据的应用程序应该使用 TemporaryFile
创建文件。Unix 下,存放该函数创建的临时文件的目录根本不会创建,或者在创建之后立即删除,其他平台不支持这个特性。这使得其他程序无法找到并打开这个文件,因为在文件系统表中并没有对它的引用。TemporaryFile
创建的文件会在文件关闭之后自动删除,通过使用 close()
方法或者在上下文管理器 with
语句中使用。
tempfile_TemporaryFile.py
import os
import tempfile
print('Building a filename with PID:')
filename = '/tmp/guess_my_name.{}.txt'.format(os.getpid())
with open(filename, 'w+b') as temp:
print('temp:')
print(' {!r}'.format(temp))
print('temp.name:')
print(' {!r}'.format(temp.name))
# 自己手动清除临时文件
os.remove(filename)
print()
print('TemporaryFile:')
with tempfile.TemporaryFile() as temp:
print('temp:')
print(' {!r}'.format(temp))
print('temp.name:')
print(' {!r}'.format(temp.name))
# 自动清除文件
上面的例子展示了使用 TemporaryFile()
方法和常规方式创建临时文件的不同。TemporaryFile()
创建的文件没有名称。
$ python3 tempfile_TemporaryFile.py
Building a filename with PID:
temp:
<_io.BufferedRandom name='/tmp/guess_my_name.12151.txt'>
temp.name:
'/tmp/guess_my_name.12151.txt'
TemporaryFile:
temp:
<_io.BufferedRandom name=4>
temp.name:
4
默认情况下,文件描述符使用 w+b
模式打开,所以它在所有平台上行为一致,调用者可以写入并从中读取。
tempfile_TemporaryFile_binary.py
import os
import tempfile
with tempfile.TemporaryFile() as temp:
temp.write(b'Some data')
temp.seek(0)
print(temp.read())
写入数据之后,文件描述符的内部指针必须重置到文件开始以便从文件读回数据。
$ python3 tempfile_TemporaryFile_binary.py
b'Some data'
可以通过在创建文件的时候设置 mode
为 w+t
而转换为文本模式。
tempfile_TemporaryFile_text.py
import tempfile
with tempfile.TemporaryFile(mode='w+t') as f:
f.writelines(['first\n', 'second\n'])
f.seek(0)
for line in f:
print(line.rstrip())
文件描述符将数据当做文本对待。
$ python3 tempfile_TemporaryFile_text.py
first
second
命名临时文件
在某些情况下,命名临时文件很重要。对于跨进程甚至跨主机应用程序来说,应用程序各部分之间最简单的数据传递方式是命名文件。 NamedTemporaryFile()
方法会创建一个文件,但不会取消链接,所以它会保留名称(可以通过 name
属性读取)。
tempfile_NamedTemporaryFile.py
import os
import pathlib
import tempfile
with tempfile.NamedTemporaryFile() as temp:
print('temp:')
print(' {!r}'.format(temp))
print('temp.name:')
print(' {!r}'.format(temp.name))
f = pathlib.Path(temp.name)
print('Exists after close:', f.exists())
文件会在句柄关闭之后删除。
$ python3 tempfile_NamedTemporaryFile.py
temp:
<tempfile._TemporaryFileWrapper object at 0x1011b2d30>
temp.name:
'/var/folders/5q/8gk0wq888xlggz008k8dr7180000hg/T/tmps4qh5zde'
Exists after close: False
缓冲文件
数临时文件通常保存量相对较小的数据,因此使用 SpooledTemporaryFile
可能效率更高,因为它在内容数量超过阈值之前,使用 io.BytesIO
或者 io.StringIO
将数据保存到内存中。当数据量超过阈值之后,数据将会被写入磁盘保存,与此同时,缓冲池也会被替换为 TemporaryFile()
。
tempfile_SpooledTemporaryFile.py
import tempfile
with tempfile.SpooledTemporaryFile(max_size=100,
mode='w+t',
encoding='utf-8') as temp:
print('temp: {!r}'.format(temp))
for i in range(3):
temp.write('This line is repeated over and over.\n')
print(temp._rolled, temp._file)
这个例子中使用了 SpooledTemporaryFile
的私有属性去检测数据何时写入到磁盘。除非调整缓冲区大小,通常是没必要的去检测这个状态。
$ python3 tempfile_SpooledTemporaryFile.py
temp: <tempfile.SpooledTemporaryFile object at 0x1007b2c88>
False <_io.StringIO object at 0x1007a3d38>
False <_io.StringIO object at 0x1007a3d38>
True <_io.TextIOWrapper name=4 mode='w+t' encoding='utf-8'>
也可以显式调用 rollover()
或者 fileno()
方法将数据写入到磁盘。
tempfile_SpooledTemporaryFile_explicit.py
import tempfile
with tempfile.SpooledTemporaryFile(max_size=1000,
mode='w+t',
encoding='utf-8') as temp:
print('temp: {!r}'.format(temp))
for i in range(3):
temp.write('This line is repeated over and over.\n')
print(temp._rolled, temp._file)
print('rolling over')
temp.rollover()
print(temp._rolled, temp._file)
这个例子中,因为缓冲池容量远大于数据量,除非显式调用 rollover()
否则不会创建文件。
$ python3 tempfile_SpooledTemporaryFile_explicit.py
temp: <tempfile.SpooledTemporaryFile object at 0x1007b2c88>
False <_io.StringIO object at 0x1007a3d38>
False <_io.StringIO object at 0x1007a3d38>
False <_io.StringIO object at 0x1007a3d38>
rolling over
True <_io.TextIOWrapper name=4 mode='w+t' encoding='utf-8'>
临时目录
当需要几个临时文件的时候,使用 TemporaryDirectory
创建一个临时目录并打开所有文件可能会更方便。
tempfile_TemporaryDirectory.py
import pathlib
import tempfile
with tempfile.TemporaryDirectory() as directory_name:
the_dir = pathlib.Path(directory_name)
print(the_dir)
a_file = the_dir / 'a_file.txt'
a_file.write_text('This file is deleted.')
print('Directory exists after?', the_dir.exists())
print('Contents after:', list(the_dir.glob('*')))
上下文管理器生成了目录的名称,然后可以在上下文中使用该名称创建其它文件。
$ python3 tempfile_TemporaryDirectory.py
/var/folders/5q/8gk0wq888xlggz008k8dr7180000hg/T/tmp_urhiioj
Directory exists after? False
Contents after: []
预测名称
虽然生成的临时文件名称中包含可预测部分比起严格匿名文件安全性较低,但可以找到该文件并对其进行检查以用于调试。到目前讲述的所有方法都采用三个参数在一定程度上控制文件名,名称生成规则如下:
dir + prefix + random + suffix
上面的四个参数中除了 random
都可以传递给相应方法用于创建临时文件和目录。
tempfile_NamedTemporaryFile_args.py
import tempfile
with tempfile.NamedTemporaryFile(suffix='_suffix',
prefix='prefix_',
dir='/tmp') as temp:
print('temp:')
print(' ', temp)
print('temp.name:')
print(' ', temp.name)
prefix
和 suffix
以及随机字符串组合起来用于构建文件名称,dir
参数用于新文件创建的位置。
$ python3 tempfile_NamedTemporaryFile_args.py
temp:
<tempfile._TemporaryFileWrapper object at 0x1018b2d68>
temp.name:
/tmp/prefix_q6wd5czl_suffix
临时文件位置
如果没有显式地使用 dir
参数设置临时文件存放的目录,那么临时文件存放的位置将基于操作系统设置。tempfile
模块提供了两个方法用于在运行时查询这个设置。
tempfile_settings.py
import tempfile
print('gettempdir():', tempfile.gettempdir())
print('gettempprefix():', tempfile.gettempprefix())
gettempdir()
方法返回默认的临时文件存放位置, gettempprefix()
返回新临时文件和目录名称的前缀。
$ python3 tempfile_settings.py
gettempdir(): /var/folders/5q/8gk0wq888xlggz008k8dr7180000hg/T
gettempprefix(): tmp
gettempdir()
通过直接查询一个列表用来确定当前进程新创建文件的存放位置。搜索的列表依次是:
- 环境变量:
TMPDIR
- 环境变量:
TEMP
- 环境变量:
TMP
- 系统备选值。( Windows 上采取
C:\temp
,C:\tmp
,\temp
, 或者\tmp
中的第一个可用值。其他平台使用/tmp
,/var/tmp
, 或者/usr/tmp
。) - 如果以上都不可用,那么就会使用当前目录。
tempfile_tempdir.py
import tempfile
tempfile.tempdir = '/I/changed/this/path'
print('gettempdir():', tempfile.gettempdir())
需要全局目录存储所有临时文件且不依赖环境变量的程序应该手动设置 tempfile.tempdir
,直接给其赋值即可。
$ python3 tempfile_tempdir.py
gettempdir(): /I/changed/this/path
推荐阅读
- tempfile 标准库文档
random
-- 随机数生成器,用户在临时文件名称中插入随机串。
本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。