Python 标准库
- 函数式编程模块
itertools
—- 为高效循环而创建迭代器的函数functools
—- 高阶函数和可调用对象上的操作operator
—- 标准运算符替代函数
- 文件和目录访问
pathlib
—- 面向对象的文件系统路径os.path
—- 常用路径操作fileinput
—- 迭代来自多个输入流的行stat
—- 解析stat()
结果filecmp
—- 文件及目录的比较tempfile
—- 生成临时文件和目录glob
—- Unix 风格路径名模式扩展fnmatch
—- Unix 文件名模式匹配linecache
—- 随机读写文本行shutil
—- 高阶文件操作
- 数据持久化
pickle
—- Python 对象序列化copyreg
—- 注册配合pickle
模块使用的函数shelve
—- Python 对象持久化marshal
—- 内部 Python 对象序列化dbm
—- Unix “数据库” 接口sqlite3
—- SQLite 数据库 DB-API 2.0 接口模块
- 数据压缩和存档
zlib
—- 与 gzip 兼容的压缩gzip
—- 对 gzip 格式的支持bz2
—- 对 bzip2 压缩算法的支持lzma
—- 用 LZMA 算法压缩zipfile
—- 使用ZIP存档tarfile
—- 读写tar归档文件
- 文件格式
csv
—- CSV 文件读写configparser
—- 配置文件解析器netrc
—- netrc 文件处理xdrlib
—- 编码与解码 XDR 数据plistlib
—- 生成与解析 Apple.plist
文件
- 加密服务
hashlib
—- 安全哈希与消息摘要hmac
—- 基于密钥的消息验证secrets
—- 生成管理密码的安全随机数
- 通用操作系统服务
os
—- 多种操作系统接口io
—- 处理流的核心工具time
—- 时间的访问和转换argparse
—- 命令行选项、参数和子命令解析器getopt
—- C 风格的命令行选项解析器logging
—- Python 的日志记录工具logging.config
—- 日志记录配置logging.handlers
—- 日志处理程序getpass
—- 便携式密码输入工具curses
—- 终端字符单元显示的处理curses.textpad
—- 用于 curses 程序的文本输入控件curses.ascii
—- 用于 ASCII 字符的工具curses.panel
—- curses 的面板栈扩展platform
—- 获取底层平台的标识数据errno
—- 标准 errno 系统符号ctypes
—- Python 的外部函数库
函数式编程模块
itertools
—- 为高效循环而创建迭代器的函数- Itertool函数
- itertools 配方
functools
—- 高阶函数和可调用对象上的操作partial
对象
operator
—- 标准运算符替代函数- 将运算符映射到函数
- 原地运算符
itertools
—- 为高效循环而创建迭代器的函数
本模块实现一系列 iterator ,这些迭代器受到APL,Haskell和SML的启发。为了适用于Python,它们都被重新写过。
本模块标准化了一个快速、高效利用内存的核心工具集,这些工具本身或组合都很有用。它们一起形成了“迭代器代数”,这使得在纯Python中有可能创建简洁又高效的专用工具。
例如,SML有一个制表工具: tabulate(f)
,它可产生一个序列 f(0), f(1), ...
。在Python中可以组合 map()
和 count()
实现: map(f, count())
。
这些内置工具同时也能很好地与 operator
模块中的高效函数配合使用。例如,我们可以将两个向量的点积映射到乘法运算符: sum(map(operator.mul, vector1, vector2))
。
无穷迭代器:
迭代器 | 实参 | 结果 | 示例 |
---|---|---|---|
count() |
start, [step] | start, start+step, start+2*step, … | count(10) —> 10 11 12 13 14 … |
cycle() |
p | p0, p1, … plast, p0, p1, … | cycle(‘ABCD’) —> A B C D A B C D … |
repeat() |
elem [,n] | elem, elem, elem, … 重复无限次或n次 | repeat(10, 3) —> 10 10 10 |
根据最短输入序列长度停止的迭代器:
迭代器 | 实参 | 结果 | 示例 |
---|---|---|---|
accumulate() |
p [,func] | p0, p0+p1, p0+p1+p2, … | accumulate([1,2,3,4,5]) —> 1 3 6 10 15 |
chain() |
p, q, … | p0, p1, … plast, q0, q1, … | chain(‘ABC’, ‘DEF’) —> A B C D E F |
chain.from_iterable() |
iterable — 可迭代对象 | p0, p1, … plast, q0, q1, … | chain.from_iterable([‘ABC’, ‘DEF’]) —> A B C D E F |
compress() |
data, selectors | (d[0] if s[0]), (d[1] if s[1]), … | compress(‘ABCDEF’, [1,0,1,0,1,1]) —> A C E F |
dropwhile() |
pred, seq | seq[n], seq[n+1], … 从pred首次真值测试失败开始 | dropwhile(lambda x: x<5, [1,4,6,4,1]) —> 6 4 1 |
filterfalse() |
pred, seq | seq中pred(x)为假值的元素,x是seq中的元素。 | filterfalse(lambda x: x%2, range(10)) —> 0 2 4 6 8 |
groupby() |
iterable[, key] | 根据key(v)值分组的迭代器 | |
islice() |
seq, [start,] stop [, step] | seq[start:stop:step]中的元素 | islice(‘ABCDEFG’, 2, None) —> C D E F G |
pairwise() |
iterable — 可迭代对象 | (p[0], p[1]), (p[1], p[2]) | pairwise(‘ABCDEFG’) —> AB BC CD DE EF FG |
starmap() |
func, seq | func(seq[0]), func(seq[1]), … | starmap(pow, [(2,5), (3,2), (10,3)]) —> 32 9 1000 |
takewhile() |
pred, seq | seq[0], seq[1], …, 直到pred真值测试失败 | takewhile(lambda x: x<5, [1,4,6,4,1]) —> 1 4 |
tee() |
it, n | it1, it2, … itn 将一个迭代器拆分为n个迭代器 | |
zip_longest() |
p, q, … | (p[0], q[0]), (p[1], q[1]), … | zip_longest(‘ABCD’, ‘xy’, fillvalue=’-‘) —> Ax By C- D- |
排列组合迭代器:
迭代器 | 实参 | 结果 |
---|---|---|
product() |
p, q, … [repeat=1] | 笛卡尔积,相当于嵌套的for循环 |
permutations() |
p[, r] | 长度r元组,所有可能的排列,无重复元素 |
combinations() |
p, r | 长度r元组,有序,无重复元素 |
combinations_with_replacement() |
p, r | 长度r元组,有序,元素可重复 |
例子 | 结果 |
---|---|
product(‘ABCD’, repeat=2) |
AA AB AC AD BA BB BC BD CA CB CC CD DA DB DC DD |
permutations(‘ABCD’, 2) |
AB AC AD BA BC BD CA CB CD DA DB DC |
combinations(‘ABCD’, 2) |
AB AC AD BC BD CD |
combinations_with_replacement(‘ABCD’, 2) |
AA AB AC AD BB BC BD CC CD DD |
Itertool函数
下列模块函数均创建并返回迭代器。有些迭代器不限制输出流长度,所以它们只应在能截断输出流的函数或循环中使用。
itertools.accumulate
(iterable[, func, **, initial=None*])
创建一个迭代器,返回累积汇总值或其他双目运算函数的累积结果值(通过可选的 func 参数指定)。
如果提供了 func*,它应当为带有两个参数的函数。 输入 *iterable 的元素可以是能被 func 接受为参数的任意类型。 (例如,对于默认的加法运算,元素可以是任何可相加的类型包括 Decimal
或 Fraction
。)
通常,输出的元素数量与输入的可迭代对象是一致的。 但是,如果提供了关键字参数 initial*,则累加会以 *initial 值开始,这样输出就比输入的可迭代对象多一个元素。
大致相当于:
def accumulate(iterable, func=operator.add,*, initial=None):
'Return running totals'
# accumulate([1,2,3,4,5]) --> 1 3 6 10 15
# accumulate([1,2,3,4,5], initial=100) --> 100 101 103 106 110 115
# accumulate([1,2,3,4,5], operator.mul) --> 1 2 6 24 120
it = iter(iterable)
total = initial
if initial isNone:
try:
total =next(it)
exceptStopIteration:
return
yield total
for element in it:
total = func(total, element)
yield total
func 参数有几种用法。它可以被设为 min()
最终得到一个最小值,或者设为 max()
最终得到一个最大值,或设为 operator.mul()
最终得到一个乘积。摊销表可通过累加利息和支付款项得到。给iterable设置初始值并只将参数 func 设为累加总数可以对一阶 递归关系 建模。
>>> data =[3,4,6,2,1,9,0,7,5,8]
>>> list(accumulate(data,operator.mul))# running product
[3,12,72,144,144,1296,0,0,0,0]
>>> list(accumulate(data, max))# running maximum
[3,4,6,6,6,9,9,9,9,9]
# Amortize a 5% loan of 1000 with 4 annual payments of 90
>>> cashflows =[1000,-90,-90,-90,-90]
>>> list(accumulate(cashflows,lambda bal, pmt: bal*1.05+ pmt))
[1000,960.0,918.0,873.9000000000001,827.5950000000001]
# Chaotic recurrence relation https://en.wikipedia.org/wiki/Logistic_map
>>> logistic_map =lambda x, _: r * x *(1- x)
>>> r =3.8
>>> x0 =0.4
>>> inputs = repeat(x0,36)# only the initial value is used
>>>[format(x,'.2f')for x in accumulate(inputs, logistic_map)]
['0.40','0.91','0.30','0.81','0.60','0.92','0.29','0.79','0.63',
'0.88','0.39','0.90','0.33','0.84','0.52','0.95','0.18','0.57',
'0.93','0.25','0.71','0.79','0.63','0.88','0.39','0.91','0.32',
'0.83','0.54','0.95','0.20','0.60','0.91','0.30','0.80','0.60']
参考一个类似函数 functools.reduce()
,它只返回一个最终累积值。
3.2 新版功能.
在 3.3 版更改: 增加可选参数 func 。
在 3.8 版更改: 添加了可选的 initial 形参。
itertools.chain
(\iterables*)
创建一个迭代器,它首先返回第一个可迭代对象中所有元素,接着返回下一个可迭代对象中所有元素,直到耗尽所有可迭代对象中的元素。可将多个序列处理为单个序列。大致相当于:
def chain(*iterables):
# chain('ABC', 'DEF') --> A B C D E F
for it in iterables:
for element in it:
yield element
classmethodchain.from_iterable
(iterable)
构建类似 chain()
迭代器的另一个选择。从一个单独的可迭代参数中得到链式输入,该参数是延迟计算的。大致相当于:
def from_iterable(iterables):
# chain.from_iterable(['ABC', 'DEF']) --> A B C D E F
for it in iterables:
for element in it:
yield element
itertools.combinations
(iterable, r)
返回由输入 iterable 中元素组成长度为 r 的子序列。
组合元组会以字典顺序根据所输入 iterable 的顺序发出。 因此,如果所输入 iterable 是已排序的,组合元组也将按已排序的顺序生成。
即使元素的值相同,不同位置的元素也被认为是不同的。如果元素各自不同,那么每个组合中没有重复元素。
大致相当于:
def combinations(iterable, r):
# combinations('ABCD', 2) --> AB AC AD BC BD CD
# combinations(range(4), 3) --> 012 013 023 123
pool = tuple(iterable)
n = len(pool)
if r > n:
return
indices = list(range(r))
yield tuple(pool[i]for i in indices)
whileTrue:
for i in reversed(range(r)):
if indices[i]!= i + n - r:
break
else:
return
indices[i]+=1
for j in range(i+1, r):
indices[j]= indices[j-1]+1
yield tuple(pool[i]for i in indices)
combinations()
的代码可被改写为 permutations()
过滤后的子序列,(相对于元素在输入中的位置)元素不是有序的。
def combinations(iterable, r):
pool = tuple(iterable)
n = len(pool)
for indices in permutations(range(n), r):
if sorted(indices)== list(indices):
yield tuple(pool[i]for i in indices)
当 0 <= r <= n
时,返回项的个数是 n! / r! / (n-r)!
;当 r > n
时,返回项个数为0。
itertools.combinations_with_replacement
(iterable, r)
返回由输入 iterable 中元素组成的长度为 r 的子序列,允许每个元素可重复出现。
组合元组会以字典顺序根据所输入 iterable 的顺序发出。 因此,如果所输入 iterable 是已排序的,组合元组也将按已排序的顺序生成。
不同位置的元素是不同的,即使它们的值相同。因此如果输入中的元素都是不同的话,返回的组合中元素也都会不同。
大致相当于:
def combinations_with_replacement(iterable, r):
# combinations_with_replacement('ABC', 2) --> AA AB AC BB BC CC
pool = tuple(iterable)
n = len(pool)
ifnot n and r:
return
indices =[0]* r
yield tuple(pool[i]for i in indices)
whileTrue:
for i in reversed(range(r)):
if indices[i]!= n -1:
break
else:
return
indices[i:]=[indices[i]+1]*(r - i)
yield tuple(pool[i]for i in indices)
combinations_with_replacement()
的代码可被改写为 production()
过滤后的子序列,(相对于元素在输入中的位置)元素不是有序的。
def combinations_with_replacement(iterable, r):
pool = tuple(iterable)
n = len(pool)
for indices in product(range(n), repeat=r):
if sorted(indices)== list(indices):
yield tuple(pool[i]for i in indices)
当 n > 0
时,返回项个数为 (n+r-1)! / r! / (n-1)!
.
3.1 新版功能.
itertools.compress
(data, selectors)
创建一个迭代器,它返回 data 中经 selectors 真值测试为 True
的元素。迭代器在两者较短的长度处停止。大致相当于:
def compress(data, selectors):
# compress('ABCDEF', [1,0,1,0,1,1]) --> A C E F
return(d for d, s in zip(data, selectors)if s)
3.1 新版功能.
itertools.count
(start=0, step=1)
创建一个迭代器,它从 start 值开始,返回均匀间隔的值。常用于 map()
中的实参来生成连续的数据点。此外,还用于 zip()
来添加序列号。大致相当于:
def count(start=0, step=1):
# count(10) --> 10 11 12 13 14 ...
# count(2.5, 0.5) -> 2.5 3.0 3.5 ...
n = start
whileTrue:
yield n
n += step
当对浮点数计数时,替换为乘法代码有时精度会更好,例如: (start + step * i for i in count())
。
在 3.1 版更改: 增加参数 step ,允许非整型。
itertools.cycle
(iterable)
创建一个迭代器,返回 iterable 中所有元素并保存一个副本。当取完 iterable 中所有元素,返回副本中的所有元素。无限重复。大致相当于:
def cycle(iterable):
# cycle('ABCD') --> A B C D A B C D A B C D ...
saved =[]
for element in iterable:
yield element
saved.append(element)
while saved:
for element in saved:
yield element
注意,该函数可能需要相当大的辅助空间(取决于 iterable 的长度)。
itertools.dropwhile
(predicate, iterable)
创建一个迭代器,如果 predicate 为true,迭代器丢弃这些元素,然后返回其他元素。注意,迭代器在 predicate 首次为false之前不会产生任何输出,所以可能需要一定长度的启动时间。大致相当于:
def dropwhile(predicate, iterable):
# dropwhile(lambda x: x<5, [1,4,6,4,1]) --> 6 4 1
iterable = iter(iterable)
for x in iterable:
ifnot predicate(x):
yield x
break
for x in iterable:
yield x
itertools.filterfalse
(predicate, iterable)
创建一个迭代器,只返回 iterable 中 predicate 为 False
的元素。如果 predicate 是 None
,返回真值测试为false的元素。大致相当于:
def filterfalse(predicate, iterable):
# filterfalse(lambda x: x%2, range(10)) --> 0 2 4 6 8
if predicate isNone:
predicate =bool
for x in iterable:
ifnot predicate(x):
yield x
itertools.groupby
(iterable, key=None)
创建一个迭代器,返回 iterable 中连续的键和组。key 是一个计算元素键值函数。如果未指定或为 None
,key 缺省为恒等函数(identity function),返回元素不变。一般来说,iterable 需用同一个键值函数预先排序。
groupby()
操作类似于Unix中的 uniq
。当每次 key 函数产生的键值改变时,迭代器会分组或生成一个新组(这就是为什么通常需要使用同一个键值函数先对数据进行排序)。这种行为与SQL的GROUP BY操作不同,SQL的操作会忽略输入的顺序将相同键值的元素分在同组中。
返回的组本身也是一个迭代器,它与 groupby()
共享底层的可迭代对象。因为源是共享的,当 groupby()
对象向后迭代时,前一个组将消失。因此如果稍后还需要返回结果,可保存为列表:
groups =[]
uniquekeys =[]
data = sorted(data, key=keyfunc)
for k, g in groupby(data, keyfunc):
groups.append(list(g))# Store group iterator as a list
uniquekeys.append(k)
groupby()
大致相当于:
class groupby:
# [k for k, g in groupby('AAAABBBCCDAABBB')] --> A B C D A B
# [list(g) for k, g in groupby('AAAABBBCCD')] --> AAAA BBB CC D
def __init__(self, iterable, key=None):
if key isNone:
key =lambda x: x
self.keyfunc = key
self.it = iter(iterable)
self.tgtkey =self.currkey =self.currvalue =object()
def __iter__(self):
returnself
def __next__(self):
self.id =object()
whileself.currkey ==self.tgtkey:
self.currvalue =next(self.it)# Exit on StopIteration
self.currkey =self.keyfunc(self.currvalue)
self.tgtkey =self.currkey
return(self.currkey,self._grouper(self.tgtkey,self.id))
def _grouper(self, tgtkey, id):
whileself.id is id andself.currkey == tgtkey:
yieldself.currvalue
try:
self.currvalue =next(self.it)
exceptStopIteration:
return
self.currkey =self.keyfunc(self.currvalue)
itertools.islice
(iterable, stop)
itertools.islice
(iterable, start, stop[, step])
创建一个迭代器,返回从 iterable 里选中的元素。如果 start 不是0,跳过 iterable 中的元素,直到到达 start 这个位置。之后迭代器连续返回元素,除非 step 设置的值很高导致被跳过。如果 stop 为 None
,迭代器耗光为止;否则,在指定的位置停止。与普通的切片不同,islice()
不支持将 start , stop ,或 step 设为负值。可用来从内部数据结构被压平的数据中提取相关字段(例如一个多行报告,它的名称字段出现在每三行上)。大致相当于:
def islice(iterable,*args):
# islice('ABCDEFG', 2) --> A B
# islice('ABCDEFG', 2, 4) --> C D
# islice('ABCDEFG', 2, None) --> C D E F G
# islice('ABCDEFG', 0, None, 2) --> A C E G
s = slice(*args)
start, stop, step = s.start or0, s.stop or sys.maxsize, s.step or1
it = iter(range(start, stop, step))
try:
nexti =next(it)
exceptStopIteration:
# Consume *iterable* up to the *start* position.
for i, element in zip(range(start), iterable):
pass
return
try:
for i, element in enumerate(iterable):
if i == nexti:
yield element
nexti =next(it)
exceptStopIteration:
# Consume to *stop*.
for i, element in zip(range(i +1, stop), iterable):
pass
如果 start 为 None
,迭代从0开始。如果 step 为 None
,步长缺省为1。
itertools.pairwise
(iterable)
返回从输入 iterable 中获取的连续重叠对。
输出迭代器中 2 元组的数量将比输入的数量少一个。 如果输入可迭代对象中少于两个值则它将为空。
大致相当于:
def pairwise(iterable):
# pairwise('ABCDEFG') --> AB BC CD DE EF FG
a, b = tee(iterable)
next(b,None)
return zip(a, b)
3.10 新版功能.
itertools.permutations
(iterable, r=None)
连续返回由 iterable 元素生成长度为 r 的排列。
如果 r 未指定或为 None
,r 默认设置为 iterable 的长度,这种情况下,生成所有全长排列。
排列元组会以字典顺序根据所输入 iterable 的顺序发出。 因此,如果所输入 iterable 是已排序的,组合元组也将按已排序的顺序生成。
即使元素的值相同,不同位置的元素也被认为是不同的。如果元素值都不同,每个排列中的元素值不会重复。
大致相当于:
def permutations(iterable, r=None):
# permutations('ABCD', 2) --> AB AC AD BA BC BD CA CB CD DA DB DC
# permutations(range(3)) --> 012 021 102 120 201 210
pool = tuple(iterable)
n = len(pool)
r = n if r isNoneelse r
if r > n:
return
indices = list(range(n))
cycles = list(range(n, n-r,-1))
yield tuple(pool[i]for i in indices[:r])
while n:
for i in reversed(range(r)):
cycles[i]-=1
if cycles[i]==0:
indices[i:]= indices[i+1:]+ indices[i:i+1]
cycles[i]= n - i
else:
j = cycles[i]
indices[i], indices[-j]= indices[-j], indices[i]
yield tuple(pool[i]for i in indices[:r])
break
else:
return
permutations()
的代码也可被改写为 product()
的子序列,只要将含有重复元素(来自输入中同一位置的)的项排除。
def permutations(iterable, r=None):
pool = tuple(iterable)
n = len(pool)
r = n if r isNoneelse r
for indices in product(range(n), repeat=r):
if len(set(indices))== r:
yield tuple(pool[i]for i in indices)
当 0 <= r <= n
,返回项个数为 n! / (n-r)!
;当 r > n
,返回项个数为0。
itertools.product
(\iterables, repeat=1*)
可迭代对象输入的笛卡儿积。
大致相当于生成器表达式中的嵌套循环。例如, product(A, B)
和 ((x,y) for x in A for y in B)
返回结果一样。
嵌套循环像里程表那样循环变动,每次迭代时将最右侧的元素向后迭代。这种模式形成了一种字典序,因此如果输入的可迭代对象是已排序的,笛卡尔积元组依次序发出。
要计算可迭代对象自身的笛卡尔积,将可选参数 repeat 设定为要重复的次数。例如,product(A, repeat=4)
和 product(A, A, A, A)
是一样的。
该函数大致相当于下面的代码,只不过实际实现方案不会在内存中创建中间结果。
def product(*args, repeat=1):
# product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy
# product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111
pools =[tuple(pool)for pool in args]* repeat
result =[[]]
for pool in pools:
result =[x+[y]for x in result for y in pool]
for prod in result:
yield tuple(prod)
在 product()
运行之前,它会完全耗尽输入的可迭代对象,在内存中保留值的临时池以生成结果积。 相应地,它只适用于有限的输入。
itertools.repeat
(object[, times])
创建一个迭代器,不断重复 object 。除非设定参数 times ,否则将无限重复。可用于 map()
函数中的参数,被调用函数可得到一个不变参数。也可用于 zip()
的参数以在元组记录中创建一个不变的部分。
大致相当于:
def repeat(object, times=None):
# repeat(10, 3) --> 10 10 10
if times isNone:
whileTrue:
yieldobject
else:
for i in range(times):
yieldobject
repeat 最常见的用途就是在 map 或 zip 提供一个常量流:
>>> list(map(pow, range(10), repeat(2)))
[0,1,4,9,16,25,36,49,64,81]
itertools.starmap
(function, iterable)
创建一个迭代器,使用从可迭代对象中获取的参数来计算该函数。当参数对应的形参已从一个单独可迭代对象组合为元组时(数据已被“预组对”)可用此函数代替 map()
。map()
与 starmap()
之间的区别可以类比 function(a,b)
与 function(*c)
的区别。大致相当于:
def starmap(function, iterable):
# starmap(pow, [(2,5), (3,2), (10,3)]) --> 32 9 1000
for args in iterable:
yieldfunction(*args)
itertools.takewhile
(predicate, iterable)
创建一个迭代器,只要 predicate 为真就从可迭代对象中返回元素。大致相当于:
def takewhile(predicate, iterable):
# takewhile(lambda x: x<5, [1,4,6,4,1]) --> 1 4
for x in iterable:
if predicate(x):
yield x
else:
break
itertools.tee
(iterable, n=2)
从一个可迭代对象中返回 n 个独立的迭代器。
下面的Python代码能帮助解释 tee 做了什么(尽管实际的实现更复杂,而且仅使用了一个底层的 FIFO 队列)。
大致相当于:
def tee(iterable, n=2):
it = iter(iterable)
deques =[collections.deque()for i in range(n)]
def gen(mydeque):
whileTrue:
ifnot mydeque:# when the local deque is empty
try:
newval =next(it)# fetch a new value and
exceptStopIteration:
return
for d in deques:# load it to all the deques
d.append(newval)
yield mydeque.popleft()
return tuple(gen(d)for d in deques)
一旦 tee()
实施了一次分裂,原有的 iterable 不应再被使用;否则tee对象无法得知 iterable 可能已向后迭代。
tee
迭代器不是线程安全的。当同时使用由同一个 tee()
调用所返回的迭代器时可能引发 RuntimeError
,即使原本的 iterable 是线程安全的。
该迭代工具可能需要相当大的辅助存储空间(这取决于要保存多少临时数据)。通常,如果一个迭代器在另一个迭代器开始之前就要使用大部份或全部数据,使用 list()
会比 tee()
更快。
itertools.zip_longest
(\iterables, fillvalue=None*)
创建一个迭代器,从每个可迭代对象中收集元素。如果可迭代对象的长度未对齐,将根据 fillvalue 填充缺失值。迭代持续到耗光最长的可迭代对象。大致相当于:
def zip_longest(*args, fillvalue=None):
# zip_longest('ABCD', 'xy', fillvalue='-') --> Ax By C- D-
iterators =[iter(it)for it in args]
num_active = len(iterators)
ifnot num_active:
return
whileTrue:
values =[]
for i, it in enumerate(iterators):
try:
value =next(it)
exceptStopIteration:
num_active -=1
ifnot num_active:
return
iterators[i]= repeat(fillvalue)
value = fillvalue
values.append(value)
yield tuple(values)
如果其中一个可迭代对象有无限长度,zip_longest()
函数应封装在限制调用次数的场景中(例如 islice()
或 takewhile()
)。除非指定, fillvalue 默认为 None
。
itertools 配方
本节将展示如何使用现有的 itertools 作为基础构件来创建扩展的工具集。
基本上所有这些西方和许许多多其他的配方都可以通过 Python Package Index 上的 more-itertools 项目 来安装:
pip install more-itertools
扩展的工具提供了与底层工具集相同的高性能。保持了超棒的内存利用率,因为一次只处理一个元素,而不是将整个可迭代对象加载到内存。代码量保持得很小,以函数式风格将这些工具连接在一起,有助于消除临时变量。速度依然很快,因为倾向于使用“矢量化”构件来取代解释器开销大的 for 循环和 generator 。
def take(n, iterable):
"Return first n items of the iterable as a list"
return list(islice(iterable, n))
def prepend(value, iterator):
"Prepend a single value in front of an iterator"
# prepend(1, [2, 3, 4]) -> 1 2 3 4
return chain([value], iterator)
def tabulate(function, start=0):
"Return function(0), function(1), ..."
return map(function, count(start))
def tail(n, iterable):
"Return an iterator over the last n items"
# tail(3, 'ABCDEFG') --> E F G
return iter(collections.deque(iterable, maxlen=n))
def consume(iterator, n=None):
"Advance the iterator n-steps ahead. If n is None, consume entirely."
# Use functions that consume iterators at C speed.
if n isNone:
# feed the entire iterator into a zero-length deque
collections.deque(iterator, maxlen=0)
else:
# advance to the empty slice starting at position n
next(islice(iterator, n, n),None)
def nth(iterable, n,default=None):
"Returns the nth item or a default value"
returnnext(islice(iterable, n,None),default)
def all_equal(iterable):
"Returns True if all the elements are equal to each other"
g = groupby(iterable)
returnnext(g,True)andnotnext(g,False)
def quantify(iterable, pred=bool):
"Count how many times the predicate is true"
return sum(map(pred, iterable))
def pad_none(iterable):
"""Returns the sequence elements and then returns None indefinitely.
Useful for emulating the behavior of the built-in map() function.
"""
return chain(iterable, repeat(None))
def ncycles(iterable, n):
"Returns the sequence elements n times"
return chain.from_iterable(repeat(tuple(iterable), n))
def dotproduct(vec1, vec2):
return sum(map(operator.mul, vec1, vec2))
def convolve(signal, kernel):
# See: https://betterexplained.com/articles/intuitive-convolution/
# convolve(data, [0.25, 0.25, 0.25, 0.25]) --> Moving average (blur)
# convolve(data, [1, -1]) --> 1st finite difference (1st derivative)
# convolve(data, [1, -2, 1]) --> 2nd finite difference (2nd derivative)
kernel = tuple(kernel)[::-1]
n = len(kernel)
window = collections.deque([0], maxlen=n)* n
for x in chain(signal, repeat(0, n-1)):
window.append(x)
yield sum(map(operator.mul, kernel, window))
def flatten(list_of_lists):
"Flatten one level of nesting"
return chain.from_iterable(list_of_lists)
def repeatfunc(func, times=None,*args):
"""Repeat calls to func with specified arguments.
Example: repeatfunc(random.random)
"""
if times isNone:
return starmap(func, repeat(args))
return starmap(func, repeat(args, times))
def grouper(iterable, n, fillvalue=None):
"Collect data into non-overlapping fixed-length chunks or blocks"
# grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx
args =[iter(iterable)]* n
return zip_longest(*args, fillvalue=fillvalue)
def triplewise(iterable):
"Return overlapping triplets from an iterable"
# triplewise('ABCDEFG') -> ABC BCD CDE DEF EFG
for(a, _),(b, c)in pairwise(pairwise(iterable)):
yield a, b, c
def sliding_window(iterable, n):
# sliding_window('ABCDEFG', 4) -> ABCD BCDE CDEF DEFG
it = iter(iterable)
window = collections.deque(islice(it, n), maxlen=n)
if len(window)== n:
yield tuple(window)
for x in it:
window.append(x)
yield tuple(window)
def roundrobin(*iterables):
"roundrobin('ABC', 'D', 'EF') --> A D E B F C"
# Recipe credited to George Sakkis
num_active = len(iterables)
nexts = cycle(iter(it).__next__ for it in iterables)
while num_active:
try:
fornextin nexts:
yieldnext()
exceptStopIteration:
# Remove the iterator we just exhausted from the cycle.
num_active -=1
nexts = cycle(islice(nexts, num_active))
def partition(pred, iterable):
"Use a predicate to partition entries into false entries and true entries"
# partition(is_odd, range(10)) --> 0 2 4 6 8 and 1 3 5 7 9
t1, t2 = tee(iterable)
return filterfalse(pred, t1), filter(pred, t2)
def before_and_after(predicate, it):
""" Variant of takewhile() that allows complete
access to the remainder of the iterator.
>>> it = iter('ABCdEfGhI')
>>> all_upper, remainder = before_and_after(str.isupper, it)
>>> ''.join(all_upper)
'ABC'
>>> ''.join(remainder) # takewhile() would lose the 'd'
'dEfGhI'
Note that the first iterator must be fully
consumed before the second iterator can
generate valid results.
"""
it = iter(it)
transition =[]
def true_iterator():
for elem in it:
if predicate(elem):
yield elem
else:
transition.append(elem)
return
def remainder_iterator():
yieldfrom transition
yieldfrom it
return true_iterator(), remainder_iterator()
def powerset(iterable):
"powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
s = list(iterable)
return chain.from_iterable(combinations(s, r)for r in range(len(s)+1))
def unique_everseen(iterable, key=None):
"List unique elements, preserving order. Remember all elements ever seen."
# unique_everseen('AAAABBBCCDAABBB') --> A B C D
# unique_everseen('ABBCcAD', str.lower) --> A B C D
seen =set()
seen_add = seen.add
if key isNone:
for element in filterfalse(seen.__contains__, iterable):
seen_add(element)
yield element
else:
for element in iterable:
k = key(element)
if k notin seen:
seen_add(k)
yield element
def unique_justseen(iterable, key=None):
"List unique elements, preserving order. Remember only the element just seen."
# unique_justseen('AAAABBBCCDAABBB') --> A B C D A B
# unique_justseen('ABBCcAD', str.lower) --> A B C A D
return map(next, map(operator.itemgetter(1), groupby(iterable, key)))
def iter_except(func, exception, first=None):
""" Call a function repeatedly until an exception is raised.
Converts a call-until-exception interface to an iterator interface.
Like builtins.iter(func, sentinel) but uses an exception instead
of a sentinel to end the loop.
Examples:
iter_except(functools.partial(heappop, h), IndexError) # priority queue iterator
iter_except(d.popitem, KeyError) # non-blocking dict iterator
iter_except(d.popleft, IndexError) # non-blocking deque iterator
iter_except(q.get_nowait, Queue.Empty) # loop over a producer Queue
iter_except(s.pop, KeyError) # non-blocking set iterator
"""
try:
if first isnotNone:
yield first()# For database APIs needing an initial cast to db.first()
whileTrue:
yield func()
except exception:
pass
def first_true(iterable,default=False, pred=None):
"""Returns the first true value in the iterable.
If no true value is found, returns *default*
If *pred* is not None, returns the first item
for which pred(item) is true.
"""
# first_true([a,b,c], x) --> a or b or c or x
# first_true([a,b], x, f) --> a if f(a) else b if f(b) else x
returnnext(filter(pred, iterable),default)
def random_product(*args, repeat=1):
"Random selection from itertools.product(*args, **kwds)"
pools =[tuple(pool)for pool in args]* repeat
return tuple(map(random.choice, pools))
def random_permutation(iterable, r=None):
"Random selection from itertools.permutations(iterable, r)"
pool = tuple(iterable)
r = len(pool)if r isNoneelse r
return tuple(random.sample(pool, r))
def random_combination(iterable, r):
"Random selection from itertools.combinations(iterable, r)"
pool = tuple(iterable)
n = len(pool)
indices = sorted(random.sample(range(n), r))
return tuple(pool[i]for i in indices)
def random_combination_with_replacement(iterable, r):
"Random selection from itertools.combinations_with_replacement(iterable, r)"
pool = tuple(iterable)
n = len(pool)
indices = sorted(random.choices(range(n), k=r))
return tuple(pool[i]for i in indices)
def nth_combination(iterable, r, index):
"Equivalent to list(combinations(iterable, r))[index]"
pool = tuple(iterable)
n = len(pool)
if r <0or r > n:
raiseValueError
c =1
k = min(r, n-r)
for i in range(1, k+1):
c = c *(n - k + i)// i
if index <0:
index += c
if index <0or index >= c:
raiseIndexError
result =[]
while r:
c, n, r = c*r//n, n-1, r-1
while index >= c:
index -= c
c, n = c*(n-r)//n, n-1
result.append(pool[-1-n])
return tuple(result)
functools
—- 高阶函数和可调用对象上的操作
源代码: Lib/functools.py
functools
模块应用于高阶函数,即参数或(和)返回值为其他函数的函数。 通常来说,此模块的功能适用于所有可调用对象。
functools
模块定义了以下函数:
@functools.cache
(user_function)
简单轻量级未绑定函数缓存。 有时称为 “memoize”。
返回值与 lru_cache(maxsize=None)
相同,创建一个查找函数参数的字典的简单包装器。 因为它不需要移出旧值,所以比带有大小限制的 lru_cache()
更小更快。
例如:
@cache
def factorial(n):
return n * factorial(n-1) if n else 1
>>> factorial(10) # no previously cached result, makes 11 recursive calls
3628800
>>> factorial(5) # just looks up cached value result
120
>>> factorial(12) # makes two new recursive calls, the other 10 are cached
479001600
3.9 新版功能.
@functools.cached_property
(func)
将一个类方法转换为特征属性,一次性计算该特征属性的值,然后将其缓存为实例生命周期内的普通属性。 类似于 property()
但增加了缓存功能。 对于在其他情况下实际不可变的高计算资源消耗的实例特征属性来说该函数非常有用。
示例:
class DataSet:
def __init__(self, sequence_of_numbers):
self._data = tuple(sequence_of_numbers)
@cached_property
def stdev(self):
return statistics.stdev(self._data)
cached_property()
的设定与 property()
有所不同。 常规的 property 会阻止属性写入,除非定义了 setter。 与之相反,cached_property 则允许写入。
cached_property 装饰器仅在执行查找且不存在同名属性时才会运行。 当运行时,cached_property 会写入同名的属性。 后续的属性读取和写入操作会优先于 cached_property 方法,其行为就像普通的属性一样。
缓存的值可通过删除该属性来清空。 这允许 cached_property 方法再次运行。
注意,这个装饰器会影响 PEP 412 键共享字典的操作。 这意味着相应的字典实例可能占用比通常时更多的空间。
而且,这个装饰器要求每个实例上的 __dict__
是可变的映射。 这意味着它将不适用于某些类型,例如元类(因为类型实例上的 __dict__
属性是类命名空间的只读代理),以及那些指定了 __slots__
但未包括 __dict__
作为所定义的空位之一的类(因为这样的类根本没有提供 __dict__
属性)。
如果可变的映射不可用或者如果想要节省空间的键共享,可以通过在 cache()
之上堆叠一个 property()
来实现类似 cached_property()
的效果:
class DataSet:
def __init__(self, sequence_of_numbers):
self._data = sequence_of_numbers
@property
@cache
def stdev(self):
return statistics.stdev(self._data)
3.8 新版功能.
functools.cmp_to_key
(func)
将(旧式的)比较函数转换为新式的 key function . 在类似于 sorted()
, min()
, max()
, heapq.nlargest()
, heapq.nsmallest()
, itertools.groupby()
等函数的 key 参数中使用。此函数主要用作将 Python 2 程序转换至新版的转换工具,以保持对比较函数的兼容。
比较函数意为一个可调用对象,该对象接受两个参数并比较它们,结果为小于则返回一个负数,相等则返回零,大于则返回一个正数。key function则是一个接受一个参数,并返回另一个用以排序的值的可调用对象。
示例:
sorted(iterable, key=cmp_to_key(locale.strcoll)) # locale-aware sort order
3.2 新版功能.
@``functools.lru_cache
(user_function)
@``functools.lru_cache
(maxsize=128, typed=False)
一个为函数提供缓存功能的装饰器,缓存 maxsize 组传入参数,在下次以相同参数调用时直接返回上一次的结果。用以节约高开销或I/O函数的调用时间。
由于使用了字典存储缓存,所以该函数的固定参数和关键字参数必须是可哈希的。
不同模式的参数可能被视为不同从而产生多个缓存项,例如, f(a=1, b=2) 和 f(b=2, a=1) 因其参数顺序不同,可能会被缓存两次。
如果指定了 user_function*,它必须是一个可调用对象。 这允许 *lru_cache 装饰器被直接应用于一个用户自定义函数,让 maxsize 保持其默认值 128:
@lru_cache
def count_vowels(sentence):
return sum(sentence.count(vowel) for vowel in 'AEIOUaeiou')
如果 maxsize 设为 None
,LRU 特性将被禁用且缓存可无限增长。
如果 typed 被设为真值,则不同类型的函数参数将被分别缓存。 例如,f(3)
和 f(3.0)
将总是会被当作具有不同结果的不同调用。 如果 typed 为假值,则具体实现通常会把它们当作相同调用并且只缓存一个结果,虽然并不一定总是会这样做。
被包装的函数配有一个 cache_parameters()
函数,该函数返回一个新的 dict
用来显示 maxsize 和 typed 的值。 这只是出于显示信息的目的。 改变值没有任何效果。
为了帮助衡量缓存的有效性以及调整 maxsize 形参,被包装的函数会带有一个 cache_info()
函数,它返回一个 named tuple 以显示 hits, misses, maxsize 和 currsize。
该装饰器也提供了一个用于清理/使缓存失效的函数 cache_clear()
。
原始的未经装饰的函数可以通过 __wrapped__
属性访问。它可以用于检查、绕过缓存,或使用不同的缓存再次装饰原始函数。
缓存会保持对参数的引用并返回值,直到它们结束生命期退出缓存或者直到缓存被清空。
LRU(最久未使用算法)缓存 在最近的调用是即将到来的调用的最佳预测值时性能最好(例如,新闻服务器上最热门文章倾向于每天更改)。 缓存的大小限制可确保缓存不会在长期运行进程如网站服务器上无限制地增长。
一般来说,LRU缓存只在当你想要重用之前计算的结果时使用。因此,用它缓存具有副作用的函数、需要在每次调用时创建不同、易变的对象的函数或者诸如time()或random()之类的不纯函数是没有意义的。
静态 Web 内容的 LRU 缓存示例:
@lru_cache(maxsize=32)
def get_pep(num):
'Retrieve text of a Python Enhancement Proposal'
resource = 'https://www.python.org/dev/peps/pep-%04d/' % num
try:
with urllib.request.urlopen(resource) as s:
return s.read()
except urllib.error.HTTPError:
return 'Not Found'
>>> for n in 8, 290, 308, 320, 8, 218, 320, 279, 289, 320, 9991:
... pep = get_pep(n)
... print(n, len(pep))
>>> get_pep.cache_info()
CacheInfo(hits=3, misses=8, maxsize=32, currsize=8)
@lru_cache(maxsize=None)
def fib(n):
if n < 2:
return n
return fib(n-1) + fib(n-2)
>>> [fib(n) for n in range(16)]
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610]
>>> fib.cache_info()
CacheInfo(hits=28, misses=16, maxsize=None, currsize=16)
3.2 新版功能.
在 3.3 版更改: 添加 typed 选项。
在 3.8 版更改: 添加了 user_function 选项。
3.9 新版功能: 新增函数 cache_parameters()
@``functools.total_ordering
给定一个声明一个或多个全比较排序方法的类,这个类装饰器实现剩余的方法。这减轻了指定所有可能的全比较操作的工作。
此类必须包含以下方法之一:__lt__()
、__le__()
、__gt__()
或 __ge__()
。另外,此类必须支持 __eq__()
方法。
例如:
@total_ordering
class Student:
def _is_valid_operand(self, other):
return (hasattr(other, "lastname") and
hasattr(other, "firstname"))
def __eq__(self, other):
if not self._is_valid_operand(other):
return NotImplemented
return ((self.lastname.lower(), self.firstname.lower()) ==
(other.lastname.lower(), other.firstname.lower()))
def __lt__(self, other):
if not self._is_valid_operand(other):
return NotImplemented
return ((self.lastname.lower(), self.firstname.lower()) <
(other.lastname.lower(), other.firstname.lower()))
注解
虽然此装饰器使得创建具有良好行为的完全有序类型变得非常容易,但它 确实 是以执行速度更缓慢和派生比较方法的堆栈回溯更复杂为代价的。 如果性能基准测试表明这是特定应用的瓶颈所在,则改为实现全部六个富比较方法应该会轻松提升速度。
注解
这个装饰器不会尝试重载类 或其上级类 中已经被声明的方法。 这意味着如果某个上级类定义了比较运算符,则 total_ordering 将不会再次实现它,即使原方法是抽象方法。
3.2 新版功能.
在 3.4 版更改: 现在已支持从未识别类型的下层比较函数返回 NotImplemented 异常。
functools.partial
(func, /, \args, *keywords)
返回一个新的 部分对象,当被调用时其行为类似于 func 附带位置参数 args 和关键字参数 keywords 被调用。 如果为调用提供了更多的参数,它们会被附加到 args。 如果提供了额外的关键字参数,它们会扩展并重载 keywords。 大致等价于:
def partial(func, /, *args, **keywords):
def newfunc(*fargs, **fkeywords):
newkeywords = {**keywords, **fkeywords}
return func(*args, *fargs, **newkeywords)
newfunc.func = func
newfunc.args = args
newfunc.keywords = keywords
return newfunc
partial()
会被“冻结了”一部分函数参数和/或关键字的部分函数应用所使用,从而得到一个具有简化签名的新对象。 例如,partial()
可用来创建一个行为类似于 int()
函数的可调用对象,其中 base 参数默认为二:
>>> from functools import partial
>>> basetwo = partial(int, base=2)
>>> basetwo.__doc__ = 'Convert base 2 string to an int.'
>>> basetwo('10010')
18
class functools.partialmethod
(func, /, \args, *keywords)
返回一个新的 partialmethod
描述器,其行为类似 partial
但它被设计用作方法定义而非直接用作可调用对象。
func 必须是一个 descriptor 或可调用对象(同属两者的对象例如普通函数会被当作描述器来处理)。
当 func 是一个描述器(例如普通 Python 函数, classmethod()
, staticmethod()
, abstractmethod()
或其他 partialmethod
的实例)时, 对 __get__
的调用会被委托给底层的描述器,并会返回一个适当的 部分对象 作为结果。
当 func 是一个非描述器类可调用对象时,则会动态创建一个适当的绑定方法。 当用作方法时其行为类似普通 Python 函数:将会插入 self 参数作为第一个位置参数,其位置甚至会处于提供给 partialmethod
构造器的 args 和 keywords 之前。
示例:
>>> class Cell:
... def __init__(self):
... self._alive = False
... @property
... def alive(self):
... return self._alive
... def set_state(self, state):
... self._alive = bool(state)
... set_alive = partialmethod(set_state, True)
... set_dead = partialmethod(set_state, False)
...
>>> c = Cell()
>>> c.alive
False
>>> c.set_alive()
>>> c.alive
True
3.4 新版功能.
functools.reduce
(function, iterable[, initializer])
将两个参数的 function 从左至右积累地应用到 iterable 的条目,以便将该可迭代对象缩减为单一的值。 例如,reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])
是计算 ((((1+2)+3)+4)+5)
的值。 左边的参数 x 是积累值而右边的参数 y 则是来自 iterable 的更新值。 如果存在可选项 initializer*,它会被放在参与计算的可迭代对象的条目之前,并在可迭代对象为空时作为默认值。 如果没有给出 *initializer 并且 iterable 仅包含一个条目,则将返回第一项。
大致相当于:
def reduce(function, iterable, initializer=None):
it = iter(iterable)
if initializer is None:
value = next(it)
else:
value = initializer
for element in it:
value = function(value, element)
return value
请参阅 itertools.accumulate()
了解有关可产生所有中间值的迭代器。
@``functools.singledispatch
将一个函数转换为 单分派 generic function。
要定义一个泛型函数,应使用 @singledispatch
装饰器进行装饰。 请注意分派是作用于第一个参数的类型,要相应地创建你的函数:
>>> from functools import singledispatch
>>> @singledispatch
... def fun(arg, verbose=False):
... if verbose:
... print("Let me just say,", end=" ")
... print(arg)
要将重载的实现添加到函数中,请使用泛型函数的 register()
属性。 它是一个装饰器。 对于带有类型标注的函数,该装饰器将自动推断第一个参数的类型:
>>> @fun.register
... def _(arg: int, verbose=False):
... if verbose:
... print("Strength in numbers, eh?", end=" ")
... print(arg)
...
>>> @fun.register
... def _(arg: list, verbose=False):
... if verbose:
... print("Enumerate this:")
... for i, elem in enumerate(arg):
... print(i, elem)
对于不使用类型标注的代码,可以将适当的类型参数显式地传给装饰器本身:
>>> @fun.register(complex)
... def _(arg, verbose=False):
... if verbose:
... print("Better than complicated.", end=" ")
... print(arg.real, arg.imag)
...
要启用注册 lambda 和现有函数,可以使用函数形式的 register()
属性:
>>> def nothing(arg, verbose=False):
... print("Nothing.")
...
>>> fun.register(type(None), nothing)
register()
属性将返回启用了装饰器堆栈、封存的未装饰函数,并会为每个变量单独创建单元测试:
>>> @fun.register(float)
... @fun.register(Decimal)
... def fun_num(arg, verbose=False):
... if verbose:
... print("Half of your number:", end=" ")
... print(arg / 2)
...
>>> fun_num is fun
False
在调用时,泛型函数会根据第一个参数的类型进行分派:
>>> fun("Hello, world.")
Hello, world.
>>> fun("test.", verbose=True)
Let me just say, test.
>>> fun(42, verbose=True)
Strength in numbers, eh? 42
>>> fun(['spam', 'spam', 'eggs', 'spam'], verbose=True)
Enumerate this:
0 spam
1 spam
2 eggs
3 spam
>>> fun(None)
Nothing.
>>> fun(1.23)
0.615
在没有用于特定类型的已注册实现的情况下,则会使用其方法解析顺序来查找更通用的实现。 以 @singledispatch
装饰的原始函数将为最基本的 object
类型进行注册,这意味着它将在找不到更好的实现时被使用。
如果一个实现注册到了 abstract base class,虚拟子类将会被发送到该实现:
>>> from collections.abc import Mapping
>>> @fun.register
... def _(arg: Mapping, verbose=False):
... if verbose:
... print("Keys & Values")
... for key, value in arg.items():
... print(key, "=>", value)
...
>>> fun({"a": "b"})
a => b
要检查泛型函数将为给定类型选择哪个实现,请使用 dispatch()
属性:
>>> fun.dispatch(float)
<function fun_num at 0x1035a2840>
>>> fun.dispatch(dict) # note: default implementation
<function fun at 0x103fe0000>
要访问所有忆注册实现,请使用只读的 registry
属性:
>>> fun.registry.keys()
dict_keys([<class 'NoneType'>, <class 'int'>, <class 'object'>,
<class 'decimal.Decimal'>, <class 'list'>,
<class 'float'>])
>>> fun.registry[float]
<function fun_num at 0x1035a2840>
>>> fun.registry[object]
<function fun at 0x103fe0000>
3.4 新版功能.
在 3.7 版更改: register()
属性支持使用类型标注。
class functools.singledispatchmethod
(func)
将一个方法转换为 单分派 generic function。
要定义一个泛型方法,应使用 @singledispatchmethod
装饰器进行装饰。 请注意分派是作用于第一个非 self 或非 cls 参数的类型,要相应地创建你的函数:
class Negator:
@singledispatchmethod
def neg(self, arg):
raise NotImplementedError("Cannot negate a")
@neg.register
def _(self, arg: int):
return -arg
@neg.register
def _(self, arg: bool):
return not arg
@singledispatchmethod
支持与其他装饰器如 @classmethod
相嵌套。 请注意如果要允许 dispatcher.register
,则 singledispatchmethod
必须是 最外层 的装饰器。 下面的示例定义了 Negator
类,其中包含绑定到类的 neg
方法:
class Negator:
@singledispatchmethod
@classmethod
def neg(cls, arg):
raise NotImplementedError("Cannot negate a")
@neg.register
@classmethod
def _(cls, arg: int):
return -arg
@neg.register
@classmethod
def _(cls, arg: bool):
return not arg
同样的模式也被用于其他类似的装饰器: staticmethod
, abstractmethod
等等。
3.8 新版功能.
functools.update_wrapper
(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)
更新一个 wrapper 函数以使其类似于 wrapped 函数。 可选参数为指明原函数的哪些属性要直接被赋值给 wrapper 函数的匹配属性的元组,并且这些 wrapper 函数的属性将使用原函数的对应属性来更新。 这些参数的默认值是模块级常量 WRAPPER_ASSIGNMENTS
(它将被赋值给 wrapper 函数的 __module__
, __name__
, __qualname__
, __annotations__
和 __doc__
即文档字符串) 以及 WRAPPER_UPDATES
(它将更新 wrapper 函数的 __dict__
即实例字典)。
为了允许出于内省和其他目的访问原始函数(例如绕过 lru_cache()
之类的缓存装饰器),此函数会自动为 wrapper 添加一个指向被包装函数的 __wrapped__
属性。
此函数的主要目的是在 decorator 函数中用来包装被装饰的函数并返回包装器。 如果包装器函数未被更新,则被返回函数的元数据将反映包装器定义而不是原始函数定义,这通常没有什么用处。
update_wrapper()
可以与函数之外的可调用对象一同使用。 在 assigned 或 updated 中命名的任何属性如果不存在于被包装对象则会被忽略(即该函数将不会尝试在包装器函数上设置它们)。 如果包装器函数自身缺少在 updated 中命名的任何属性则仍将引发 AttributeError
。
3.2 新版功能: 自动添加 __wrapped__
属性。
3.2 新版功能: 默认拷贝 __annotations__
属性。
在 3.2 版更改: 不存在的属性将不再触发 AttributeError
。
在 3.4 版更改: __wrapped__
属性现在总是指向被包装的函数,即使该函数定义了 __wrapped__
属性。 (参见 bpo-17482)
@``functools.wraps
(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)
这是一个便捷函数,用于在定义包装器函数时发起调用 update_wrapper()
作为函数装饰器。 它等价于 partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated)
。 例如:
>>> from functools import wraps
>>> def my_decorator(f):
... @wraps(f)
... def wrapper(*args, **kwds):
... print('Calling decorated function')
... return f(*args, **kwds)
... return wrapper
...
>>> @my_decorator
... def example():
... """Docstring"""
... print('Called example function')
...
>>> example()
Calling decorated function
Called example function
>>> example.__name__
'example'
>>> example.__doc__
'Docstring'
如果不使用这个装饰器工厂函数,则 example 函数的名称将变为 'wrapper'
,并且 example()
原本的文档字符串将会丢失。
partial
对象
partial
对象是由 partial()
创建的可调用对象。 它们具有三个只读属性:
partial.func
一个可调用对象或函数。 对 partial
对象的调用将被转发给 func
并附带新的参数和关键字。
partial.args
最左边的位置参数将放置在提供给 partial
对象调用的位置参数之前。
partial.keywords
当调用 partial
对象时将要提供的关键字参数。
partial
对象与 function
对象的类似之处在于它们都是可调用、可弱引用的对象并可拥有属性。 但两者也存在一些重要的区别。 例如前者不会自动创建 __name__
和 __doc__
属性。 而且,在类中定义的 partial
对象的行为类似于静态方法,并且不会在实例属性查找期间转换为绑定方法。
operator
—- 标准运算符替代函数
源代码: Lib/operator.py
operator
模块提供了一套与Python的内置运算符对应的高效率函数。例如,operator.add(x, y)
与表达式 x+y
相同。 许多函数名与特殊方法名相同,只是没有双下划线。为了向后兼容性,也保留了许多包含双下划线的函数。为了表述清楚,建议使用没有双下划线的函数。
函数包含的种类有:对象的比较运算、逻辑运算、数学运算以及序列运算。
对象比较函数适用于所有的对象,函数名根据它们对应的比较运算符命名。
operator.lt
(a, b)
operator.le
(a, b)
operator.eq
(a, b)
operator.ne
(a, b)
operator.ge
(a, b)
operator.gt
(a, b)
operator.__lt__
(a, b)
operator.__le__
(a, b)
operator.__eq__
(a, b)
operator.__ne__
(a, b)
operator.__ge__
(a, b)
operator.__gt__
(a, b)
在 a 和 b 之间进行全比较。具体的,lt(a, b)
与 a < b
相同, le(a, b)
与 a <= b
相同,eq(a, b)
与 a == b
相同,ne(a, b)
与 a != b
相同,gt(a, b)
与 a > b
相同,ge(a, b)
与 a >= b
相同。注意这些函数可以返回任何值,无论它是否可当作布尔值。
逻辑运算通常也适用于所有对象,并且支持真值检测、标识检测和布尔运算:
operator.not_
(obj)
operator.__not__
(obj)
返回 not
obj 的结果。 (请注意对象实例并没有 __not__()
方法;只有解释器核心可定义此操作。 结果会受 __bool__()
和 __len__()
方法影响。)
operator.truth
(obj)
如果 obj 为真值则返回 True
,否则返回 False
。 这等价于使用 bool
构造器。
operator.is_
(a, b)
返回 a is b
。 检测对象标识。
operator.is_not
(a, b)
返回 a is not b
。 检测对象标识。
数学和按位运算的种类是最多的:
operator.abs
(obj)
operator.__abs__
(obj)
返回 obj 的绝对值。
operator.add
(a, b)
operator.__add__
(a, b)
对于数字 a 和 b,返回 a + b
。
operator.and_
(a, b)
operator.__and__
(a, b)
返回 x 和 y 按位与的结果。
operator.floordiv
(a, b)
operator.__floordiv__
(a, b)
返回 a // b
。
operator.index
(a)
operator.__index__
(a)
返回 a 转换为整数的结果。 等价于 a.__index__()
。
在 3.10 版更改: 结果总是为 int
类型。 在之前版本中,结果可能为 int
的子类的实例。
operator.inv
(obj)
operator.invert
(obj)
operator.__inv__
(obj)
operator.__invert__
(obj)
返回数字 obj 按位取反的结果。 这等价于 ~obj
。
operator.lshift
(a, b)
operator.__lshift__
(a, b)
返回 a 左移 b 位的结果。
operator.mod
(a, b)
operator.__mod__
(a, b)
返回 a % b
。
operator.mul
(a, b)
operator.__mul__
(a, b)
对于数字 a 和 b,返回 a * b
。
operator.matmul
(a, b)
operator.__matmul__
(a, b)
返回 a @ b
。
3.5 新版功能.
operator.neg
(obj)
operator.__neg__
(obj)
返回 obj 取负的结果 (-obj
)。
operator.or_
(a, b)
operator.__or__
(a, b)
返回 a 和 b 按位或的结果。
operator.pos
(obj)
operator.__pos__
(obj)
返回 obj 取正的结果 (+obj
)。
operator.pow
(a, b)
operator.__pow__
(a, b)
对于数字 a 和 b,返回 a ** b
。
operator.rshift
(a, b)
operator.__rshift__
(a, b)
返回 a 右移 b 位的结果。
operator.sub
(a, b)
operator.__sub__
(a, b)
返回 a - b
。
operator.truediv
(a, b)
operator.__truediv__
(a, b)
返回 a / b
例如 2/3 将等于 .66 而不是 0。 这也被称为“真”除法。
operator.xor
(a, b)
operator.__xor__
(a, b)
返回 a 和 b 按位异或的结果。
适用于序列的操作(其中一些也适用于映射)包括:
operator.concat
(a, b)
operator.__concat__
(a, b)
对于序列 a 和 b,返回 a + b
。
operator.contains
(a, b)
operator.__contains__
(a, b)
返回 b in a
检测的结果。 请注意操作数是反序的。
operator.countOf
(a, b)
返回 b 在 a 中的出现次数。
operator.delitem
(a, b)
operator.__delitem__
(a, b)
移除 a 中索引号为 b 的值。
operator.getitem
(a, b)
operator.__getitem__
(a, b)
返回 a 中索引为 b 的值。
operator.indexOf
(a, b)
返回 b 在 a 中首次出现所在的索引号。
operator.setitem
(a, b, c)
operator.__setitem__
(a, b, c)
将 a 中索引号为 b 的值设为 c。
operator.length_hint
(obj, default=0)
返回对象 o 的估计长度。 首先尝试返回其实际长度,再使用 object.__length_hint__()
得出估计值,最后返回默认值。
3.4 新版功能.
operator
模块还定义了一些用于常规属性和条目查找的工具。 这些工具适合用来编写快速字段提取器作为 map()
, sorted()
, itertools.groupby()
或其他需要相应函数参数的函数的参数。
operator.attrgetter
(attr)
operator.attrgetter
(\attrs*)
返回一个可从操作数中获取 attr 的可调用对象。 如果请求了一个以上的属性,则返回一个属性元组。 属性名称还可包含点号。 例如:
- 在
f = attrgetter('name')
之后,调用f(b)
将返回b.name
。 - 在
f = attrgetter('name', 'date')
之后,调用f(b)
将返回(b.name, b.date)
。 - 在
f = attrgetter('name.first', 'name.last')
之后,调用f(b)
将返回(b.name.first, b.name.last)
。
等价于:
def attrgetter(*items):
if any(not isinstance(item, str) for item in items):
raise TypeError('attribute name must be a string')
if len(items) == 1:
attr = items[0]
def g(obj):
return resolve_attr(obj, attr)
else:
def g(obj):
return tuple(resolve_attr(obj, attr) for attr in items)
return g
def resolve_attr(obj, attr):
for name in attr.split("."):
obj = getattr(obj, name)
return obj
operator.itemgetter
(item)
operator.itemgetter
(\items*)
返回一个使用操作数的 __getitem__()
方法从操作数中获取 item 的可调用对象。 如果指定了多个条目,则返回一个查找值的元组。 例如:
- 在
f = itemgetter(2)
之后,调用f(r)
将返回r[2]
。 - 在
g = itemgetter(2, 5, 3)
之后,调用g(r)
将返回(r[2], r[5], r[3])
。
等价于:
def itemgetter(*items):
if len(items) == 1:
item = items[0]
def g(obj):
return obj[item]
else:
def g(obj):
return tuple(obj[item] for item in items)
return g
传入的条目可以为操作数的 __getitem__()
所接受的任何类型。 字典接受任意可哈希的值。 列表、元组和字符串接受 index 或 slice 对象:
>>> itemgetter(1)('ABCDEFG')
'B'
>>> itemgetter(1, 3, 5)('ABCDEFG')
('B', 'D', 'F')
>>> itemgetter(slice(2, None))('ABCDEFG')
'CDEFG'
>>> soldier = dict(rank='captain', name='dotterbart')
>>> itemgetter('rank')(soldier)
'captain'
使用 itemgetter()
从元组的记录中提取特定字段的例子:
>>> inventory = [('apple', 3), ('banana', 2), ('pear', 5), ('orange', 1)]
>>> getcount = itemgetter(1)
>>> list(map(getcount, inventory))
[3, 2, 5, 1]
>>> sorted(inventory, key=getcount)
[('orange', 1), ('banana', 2), ('apple', 3), ('pear', 5)]
operator.methodcaller
(name, /, \args, *kwargs)
返回一个在操作数上调用 name 方法的可调用对象。 如果给出额外的参数和/或关键字参数,它们也将被传给该方法。 例如:
- 在
f = methodcaller('name')
之后,调用f(b)
将返回b.name()
。 - 在
f = methodcaller('name', 'foo', bar=1)
之后,调用f(b)
将返回b.name('foo', bar=1)
。
等价于:
def methodcaller(name, /, *args, **kwargs):
def caller(obj):
return getattr(obj, name)(*args, **kwargs)
return caller
将运算符映射到函数
以下表格显示了抽象运算是如何对应于 Python 语法中的运算符和 operator
模块中的函数的。
运算 | 语法 | 函数 |
---|---|---|
加法 | a + b |
add(a, b) |
字符串拼接 | seq1 + seq2 |
concat(seq1, seq2) |
包含测试 | obj in seq |
contains(seq, obj) |
除法 | a / b |
truediv(a, b) |
除法 | a // b |
floordiv(a, b) |
按位与 | a & b |
and*(a, b)* |
按位异或 | a ^ b |
xor(a, b) |
按位取反 | ~ a |
invert(a) |
按位或 | `a | b` |
取幂 | a ** b* |
pow(a, b) |
标识 | a is b |
is_(a, b) |
标识 | a is not b |
is_not(a, b) |
索引赋值 | obj[k] = v |
setitem(obj, k, v) |
索引删除 | del obj[k] |
delitem(obj, k) |
索引取值 | obj[k] |
getitem(obj, k) |
左移 | a << b |
lshift(a, b) |
取模 | a % b |
mod(a, b) |
乘法 | a b |
mul(a, b) |
矩阵乘法 | a @ b |
matmul(a, b) |
取反(算术) | - a |
neg(a) |
取反(逻辑) | not a |
not_(a) |
正数 | + a |
pos(a) |
右移 | a >> b |
rshift(a, b) |
切片赋值 | seq[i:j] = values |
setitem(seq, slice(i, j), values) |
切片删除 | del seq[i:j] |
delitem(seq, slice(i, j)) |
切片取值 | seq[i:j] |
getitem(seq, slice(i, j)) |
字符串格式化 | s % obj |
mod(s, obj) |
减法 | a - b |
sub(a, b) |
真值测试 | obj |
truth(obj) |
比较 | a < b |
lt(a, b) |
比较 | a <= b |
le(a, b) |
相等 | a == b |
eq(a, b) |
不等 | a != b |
ne(a, b) |
比较 | a >= b |
ge(a, b) |
比较 | a > b |
gt(a, b) |
原地运算符
许多运算都有“原地”版本。 以下列出的是提供对原地运算符相比通常语法更底层访问的函数,例如 statement x += y
相当于 x = operator.iadd(x, y)
。 换一种方式来讲就是 z = operator.iadd(x, y)
等价于语句块 z = x; z += y
。
在这些例子中,请注意当调用一个原地方法时,运算和赋值是分成两个步骤来执行的。 下面列出的原地函数只执行第一步即调用原地方法。 第二步赋值则不加处理。
对于不可变的目标例如字符串、数字和元组,更新的值会被计算,但不会被再被赋值给输入变量:
>>> a = 'hello'
>>> iadd(a, ' world')
'hello world'
>>> a
'hello'
对于可变的目标例如列表和字典,原地方法将执行更新,因此不需要后续赋值操作:
>>> s = ['h', 'e', 'l', 'l', 'o']
>>> iadd(s, [' ', 'w', 'o', 'r', 'l', 'd'])
['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd']
>>> s
['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd']
operator.iadd
(a, b)
operator.__iadd__
(a, b)
a = iadd(a, b)
等价于 a += b
。
operator.iand
(a, b)
operator.__iand__
(a, b)
a = iand(a, b)
等价于 a &= b
。
operator.iconcat
(a, b)
operator.__iconcat__
(a, b)
a = iconcat(a, b)
等价于 a += b
其中 a 和 b 为序列。
operator.ifloordiv
(a, b)
operator.__ifloordiv__
(a, b)
a = ifloordiv(a, b)
等价于 a //= b
。
operator.ilshift
(a, b)
operator.__ilshift__
(a, b)
a = ilshift(a, b)
等价于 a <<= b
。
operator.imod
(a, b)
operator.__imod__
(a, b)
a = imod(a, b)
等价于 a %= b
。
operator.imul
(a, b)
operator.__imul__
(a, b)
a = imul(a, b)
等价于 a *= b
。
operator.imatmul
(a, b)
operator.__imatmul__
(a, b)
a = imatmul(a, b)
等价于 a @= b
。
3.5 新版功能.
operator.ior
(a, b)
operator.__ior__
(a, b)
a = ior(a, b)
等价于 a |= b
。
operator.ipow
(a, b)
operator.__ipow__
(a, b)
a = ipow(a, b)
等价于 a **= b
。
operator.irshift
(a, b)
operator.__irshift__
(a, b)
a = irshift(a, b)
等价于 a >>= b
。
operator.isub
(a, b)
operator.__isub__
(a, b)
a = isub(a, b)
等价于 a -= b
。
operator.itruediv
(a, b)
operator.__itruediv__
(a, b)
a = itruediv(a, b)
等价于 a /= b
。
operator.ixor
(a, b)
operator.__ixor__
(a, b)
a = ixor(a, b)
等价于 a ^= b
。
文件和目录访问
pathlib
—- 面向对象的文件系统路径- 基础使用
- 纯路径
- 通用性质
- 运算符
- 访问个别部分
- 方法和特征属性
- 具体路径
- 方法
- 对应的
os
模块的工具
os.path
—- 常用路径操作fileinput
—- 迭代来自多个输入流的行stat
—- 解析stat()
结果filecmp
—- 文件及目录的比较dircmp
类
tempfile
—- 生成临时文件和目录- 例子
- 已弃用的函数和变量
glob
—- Unix 风格路径名模式扩展fnmatch
—- Unix 文件名模式匹配linecache
—- 随机读写文本行shutil
—- 高阶文件操作- 目录和文件操作
- 依赖于具体平台的高效拷贝操作
- copytree 示例
- rmtree 示例
- 归档操作
- 归档程序示例
- 使用 base_dir 的归档程序示例
- 查询输出终端的尺寸
- 目录和文件操作
pathlib
—- 面向对象的文件系统路径
3.4 新版功能.
源代码 Lib/pathlib.py
该模块提供表示文件系统路径的类,其语义适用于不同的操作系统。路径类被分为提供纯计算操作而没有 I/O 的 纯路径,以及从纯路径继承而来但提供 I/O 操作的 具体路径。
如果以前从未用过此模块,或不确定哪个类适合完成任务,那要用的可能就是 Path
。它在运行代码的平台上实例化为 具体路径。
在一些用例中纯路径很有用,例如:
- 如果你想要在 Unix 设备上操作 Windows 路径(或者相反)。你不应在 Unix 上实例化一个
WindowsPath
,但是你可以实例化PureWindowsPath
。 - 你只想操作路径但不想实际访问操作系统。在这种情况下,实例化一个纯路径是有用的,因为它们没有任何访问操作系统的操作。
参见
PEP 428:pathlib 模块 — 面向对象的的文件系统路径。
参见
对于底层的路径字符串操作,你也可以使用 os.path
模块。
基础使用
导入主类:
>>> from pathlib import Path
列出子目录:
>>> p = Path('.')
>>> [x for x in p.iterdir() if x.is_dir()]
[PosixPath('.hg'), PosixPath('docs'), PosixPath('dist'),
PosixPath('__pycache__'), PosixPath('build')]
列出当前目录树下的所有 Python 源代码文件:
>>> list(p.glob('**/*.py'))
[PosixPath('test_pathlib.py'), PosixPath('setup.py'),
PosixPath('pathlib.py'), PosixPath('docs/conf.py'),
PosixPath('build/lib/pathlib.py')]
在目录树中移动:
>>> p = Path('/etc')
>>> q = p / 'init.d' / 'reboot'
>>> q
PosixPath('/etc/init.d/reboot')
>>> q.resolve()
PosixPath('/etc/rc.d/init.d/halt')
查询路径的属性:
>>> q.exists()
True
>>> q.is_dir()
False
打开一个文件:
>>> with q.open() as f: f.readline()
...
'#!/bin/bash\n'
纯路径
纯路径对象提供了不实际访问文件系统的路径处理操作。有三种方式来访问这些类,也是不同的风格:
class pathlib.PurePath
(\pathsegments*)
一个通用的类,代表当前系统的路径风格(实例化为 PurePosixPath
或者 PureWindowsPath
):
>>> PurePath('setup.py') # Running on a Unix machine
PurePosixPath('setup.py')
每一个 pathsegments 的元素可能是一个代表路径片段的字符串,一个返回字符串的实现了 os.PathLike
接口的对象,或者另一个路径对象:
>>> PurePath('foo', 'some/path', 'bar')
PurePosixPath('foo/some/path/bar')
>>> PurePath(Path('foo'), Path('bar'))
PurePosixPath('foo/bar')
当 pathsegments 为空的时候,假定为当前目录:
>>> PurePath()
PurePosixPath('.')
当给出一些绝对路径,最后一位将被当作锚(模仿 os.path.join()
的行为):
>>> PurePath('/etc', '/usr', 'lib64')
PurePosixPath('/usr/lib64')
>>> PureWindowsPath('c:/Windows', 'd:bar')
PureWindowsPath('d:bar')
但是,在 Windows 路径中,改变本地根目录并不会丢弃之前盘符的设置:
>>> PureWindowsPath('c:/Windows', '/Program Files')
PureWindowsPath('c:/Program Files')
假斜线和单独的点都会被消除,但是双点 (‘..’
) 不会,以防改变符号链接的含义。
>>> PurePath('foo//bar')
PurePosixPath('foo/bar')
>>> PurePath('foo/./bar')
PurePosixPath('foo/bar')
>>> PurePath('foo/../bar')
PurePosixPath('foo/../bar')
(一个很 naïve 的做法是让 PurePosixPath('foo/../bar')
等同于 PurePosixPath('bar')
,如果 foo
是一个指向其他目录的符号链接那么这个做法就将出错)
纯路径对象实现了 os.PathLike
接口,允许它们在任何接受此接口的地方使用。
在 3.6 版更改: 添加了 os.PathLike
接口支持。
class pathlib.PurePosixPath
(\pathsegments*)
一个 PurePath
的子类,路径风格不同于 Windows 文件系统:
>>> PurePosixPath('/etc')
PurePosixPath('/etc')
pathsegments 参数的指定和 PurePath
相同。
class pathlib.PureWindowsPath
(\pathsegments*)
PurePath
的一个子类,路径风格为 Windows 文件系统路径:
>>> PureWindowsPath('c:/Program Files/')
PureWindowsPath('c:/Program Files')
pathsegments 参数的指定和 PurePath
相同。
无论你正运行什么系统,你都可以实例化这些类,因为它们提供的操作不做任何系统调用。
通用性质
路径是不可变并可哈希的。相同风格的路径可以排序与比较。这些性质尊重对应风格的大小写转换语义:
>>> PurePosixPath('foo') == PurePosixPath('FOO')
False
>>> PureWindowsPath('foo') == PureWindowsPath('FOO')
True
>>> PureWindowsPath('FOO') in { PureWindowsPath('foo') }
True
>>> PureWindowsPath('C:') < PureWindowsPath('d:')
True
不同风格的路径比较得到不等的结果并且无法被排序:
>>> PureWindowsPath('foo') == PurePosixPath('foo')
False
>>> PureWindowsPath('foo') < PurePosixPath('foo')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'PureWindowsPath' and 'PurePosixPath'
运算符
斜杠 /
操作符有助于创建子路径,就像 os.path.join()
一样:
>>> p = PurePath('/etc')
>>> p
PurePosixPath('/etc')
>>> p / 'init.d' / 'apache2'
PurePosixPath('/etc/init.d/apache2')
>>> q = PurePath('bin')
>>> '/usr' / q
PurePosixPath('/usr/bin')
文件对象可用于任何接受 os.PathLike
接口实现的地方。
>>> import os
>>> p = PurePath('/etc')
>>> os.fspath(p)
'/etc'
路径的字符串表示法为它自己原始的文件系统路径(以原生形式,例如在 Windows 下使用反斜杠)。你可以传递给任何需要字符串形式路径的函数。
>>> p = PurePath('/etc')
>>> str(p)
'/etc'
>>> p = PureWindowsPath('c:/Program Files')
>>> str(p)
'c:\\Program Files'
类似地,在路径上调用 bytes
将原始文件系统路径作为字节对象给出,就像被 os.fsencode()
编码一样:
>>> bytes(p)
b'/etc'
注解
只推荐在 Unix 下调用 bytes
。在 Windows, unicode 形式是文件系统路径的规范表示法。
访问个别部分
为了访问路径独立的部分 (组件),使用以下特征属性:
PurePath.parts
一个元组,可以访问路径的多个组件:
>>> p = PurePath('/usr/bin/python3')
>>> p.parts
('/', 'usr', 'bin', 'python3')
>>> p = PureWindowsPath('c:/Program Files/PSF')
>>> p.parts
('c:\\', 'Program Files', 'PSF')
(注意盘符和本地根目录是如何重组的)
方法和特征属性
纯路径提供以下方法和特征属性:
PurePath.drive
一个表示驱动器盘符或命名的字符串,如果存在:
>>> PureWindowsPath('c:/Program Files/').drive
'c:'
>>> PureWindowsPath('/Program Files/').drive
''
>>> PurePosixPath('/etc').drive
''
UNC 分享也被认作驱动器:
>>> PureWindowsPath('//host/share/foo.txt').drive
'\\\\host\\share'
PurePath.root
一个表示(本地或全局)根的字符串,如果存在:
>>> PureWindowsPath('c:/Program Files/').root
'\\'
>>> PureWindowsPath('c:Program Files/').root
''
>>> PurePosixPath('/etc').root
'/'
UNC 分享一样拥有根:
>>> PureWindowsPath('//host/share').root
'\\'
PurePath.anchor
驱动器和根的联合:
>>> PureWindowsPath('c:/Program Files/').anchor
'c:\\'
>>> PureWindowsPath('c:Program Files/').anchor
'c:'
>>> PurePosixPath('/etc').anchor
'/'
>>> PureWindowsPath('//host/share').anchor
'\\\\host\\share\\'
An immutable sequence providing access to the logical ancestors of the path:
>>> p = PureWindowsPath('c:/foo/bar/setup.py')
>>> p.parents[0]
PureWindowsPath('c:/foo/bar')
>>> p.parents[1]
PureWindowsPath('c:/foo')
>>> p.parents[2]
PureWindowsPath('c:/')
在 3.10 版更改: parents 序列现在支持 切片 负的索引值。
PurePath.parent
此路径的逻辑父路径:
>>> p = PurePosixPath('/a/b/c/d')
>>> p.parent
PurePosixPath('/a/b/c')
你不能超过一个 anchor 或空路径:
>>> p = PurePosixPath('/')
>>> p.parent
PurePosixPath('/')
>>> p = PurePosixPath('.')
>>> p.parent
PurePosixPath('.')
注解
这是一个单纯的词法操作,因此有以下行为:
>>> p = PurePosixPath('foo/..')
>>> p.parent
PurePosixPath('foo')
如果你想要向上移动任意文件系统路径,推荐先使用 Path.resolve()
来解析符号链接以及消除 ".."
组件。
PurePath.name
一个表示最后路径组件的字符串,排除了驱动器与根目录,如果存在的话:
>>> PurePosixPath('my/library/setup.py').name
'setup.py'
UNC 驱动器名不被考虑:
>>> PureWindowsPath('//some/share/setup.py').name
'setup.py'
>>> PureWindowsPath('//some/share').name
''
PurePath.suffix
最后一个组件的文件扩展名,如果存在:
>>> PurePosixPath('my/library/setup.py').suffix
'.py'
>>> PurePosixPath('my/library.tar.gz').suffix
'.gz'
>>> PurePosixPath('my/library').suffix
''
PurePath.suffixes
路径的文件扩展名列表:
>>> PurePosixPath('my/library.tar.gar').suffixes
['.tar', '.gar']
>>> PurePosixPath('my/library.tar.gz').suffixes
['.tar', '.gz']
>>> PurePosixPath('my/library').suffixes
[]
PurePath.stem
最后一个路径组件,除去后缀:
>>> PurePosixPath('my/library.tar.gz').stem
'library.tar'
>>> PurePosixPath('my/library.tar').stem
'library'
>>> PurePosixPath('my/library').stem
'library'
PurePath.as_posix
()
返回使用正斜杠(/
)的路径字符串:
>>> p = PureWindowsPath('c:\\windows')
>>> str(p)
'c:\\windows'
>>> p.as_posix()
'c:/windows'
PurePath.as_uri
()
将路径表示为 file
URL。如果并非绝对路径,抛出 ValueError
。
>>> p = PurePosixPath('/etc/passwd')
>>> p.as_uri()
'file:///etc/passwd'
>>> p = PureWindowsPath('c:/Windows')
>>> p.as_uri()
'file:///c:/Windows'
PurePath.is_absolute
()
返回此路径是否为绝对路径。如果路径同时拥有驱动器符与根路径(如果风格允许)则将被认作绝对路径。
>>> PurePosixPath('/a/b').is_absolute()
True
>>> PurePosixPath('a/b').is_absolute()
False
>>> PureWindowsPath('c:/a/b').is_absolute()
True
>>> PureWindowsPath('/a/b').is_absolute()
False
>>> PureWindowsPath('c:').is_absolute()
False
>>> PureWindowsPath('//some/share').is_absolute()
True
PurePath.is_relative_to
(\other*)
返回此路径是否相对于 other 的路径。
>>> p = PurePath('/etc/passwd')
>>> p.is_relative_to('/etc')
True
>>> p.is_relative_to('/usr')
False
3.9 新版功能.
PurePath.is_reserved
()
在 PureWindowsPath
,如果路径是被 Windows 保留的则返回 True
,否则 False
。在 PurePosixPath
,总是返回 False
。
>>> PureWindowsPath('nul').is_reserved()
True
>>> PurePosixPath('nul').is_reserved()
False
当保留路径上的文件系统被调用,则可能出现玄学失败或者意料之外的效应。
PurePath.joinpath
(\other*)
调用此方法等同于将每个 other 参数中的项目连接在一起:
>>> PurePosixPath('/etc').joinpath('passwd')
PurePosixPath('/etc/passwd')
>>> PurePosixPath('/etc').joinpath(PurePosixPath('passwd'))
PurePosixPath('/etc/passwd')
>>> PurePosixPath('/etc').joinpath('init.d', 'apache2')
PurePosixPath('/etc/init.d/apache2')
>>> PureWindowsPath('c:').joinpath('/Program Files')
PureWindowsPath('c:/Program Files')
PurePath.match
(pattern)
将此路径与提供的通配符风格的模式匹配。如果匹配成功则返回 True
,否则返回 False
。
如果 pattern 是相对的,则路径可以是相对路径或绝对路径,并且匹配是从右侧完成的:
>>> PurePath('a/b.py').match('*.py')
True
>>> PurePath('/a/b/c.py').match('b/*.py')
True
>>> PurePath('/a/b/c.py').match('a/*.py')
False
如果 pattern 是绝对的,则路径必须是绝对的,并且路径必须完全匹配:
>>> PurePath('/a.py').match('/*.py')
True
>>> PurePath('a/b.py').match('/*.py')
False
与其他方法一样,是否大小写敏感遵循平台的默认规则:
>>> PurePosixPath('b.py').match('*.PY')
False
>>> PureWindowsPath('b.py').match('*.PY')
True
PurePath.relative_to
(\other*)
计算此路径相对 other 表示路径的版本。如果不可计算,则抛出 ValueError:
>>> p = PurePosixPath('/etc/passwd')
>>> p.relative_to('/')
PurePosixPath('etc/passwd')
>>> p.relative_to('/etc')
PurePosixPath('passwd')
>>> p.relative_to('/usr')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "pathlib.py", line 694, in relative_to
.format(str(self), str(formatted)))
ValueError: '/etc/passwd' is not in the subpath of '/usr' OR one path is relative and the other absolute.
注意:此函数是 PurePath
的一部分并且适用于字符串。 它不会检查或访问下层的文件结构。
PurePath.with_name
(name)
返回一个新的路径并修改 name
。如果原本路径没有 name,ValueError 被抛出:
>>> p = PureWindowsPath('c:/Downloads/pathlib.tar.gz')
>>> p.with_name('setup.py')
PureWindowsPath('c:/Downloads/setup.py')
>>> p = PureWindowsPath('c:/')
>>> p.with_name('setup.py')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/antoine/cpython/default/Lib/pathlib.py", line 751, in with_name
raise ValueError("%r has an empty name" % (self,))
ValueError: PureWindowsPath('c:/') has an empty name
PurePath.with_stem
(stem)
返回一个带有修改后 stem
的新路径。 如果原路径没有名称,则会引发 ValueError:
>>> p = PureWindowsPath('c:/Downloads/draft.txt')
>>> p.with_stem('final')
PureWindowsPath('c:/Downloads/final.txt')
>>> p = PureWindowsPath('c:/Downloads/pathlib.tar.gz')
>>> p.with_stem('lib')
PureWindowsPath('c:/Downloads/lib.gz')
>>> p = PureWindowsPath('c:/')
>>> p.with_stem('')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/antoine/cpython/default/Lib/pathlib.py", line 861, in with_stem
return self.with_name(stem + self.suffix)
File "/home/antoine/cpython/default/Lib/pathlib.py", line 851, in with_name
raise ValueError("%r has an empty name" % (self,))
ValueError: PureWindowsPath('c:/') has an empty name
3.9 新版功能.
PurePath.with_suffix
(suffix)
返回一个新的路径并修改 suffix
。如果原本的路径没有后缀,新的 suffix 则被追加以代替。如果 suffix 是空字符串,则原本的后缀被移除:
>>> p = PureWindowsPath('c:/Downloads/pathlib.tar.gz')
>>> p.with_suffix('.bz2')
PureWindowsPath('c:/Downloads/pathlib.tar.bz2')
>>> p = PureWindowsPath('README')
>>> p.with_suffix('.txt')
PureWindowsPath('README.txt')
>>> p = PureWindowsPath('README.txt')
>>> p.with_suffix('')
PureWindowsPath('README')
具体路径
具体路径是纯路径的子类。除了后者提供的操作之外,它们还提供了对路径对象进行系统调用的方法。有三种方法可以实例化具体路径:
class pathlib.Path
(\pathsegments*)
一个 PurePath
的子类,此类以当前系统的路径风格表示路径(实例化为 PosixPath
或 WindowsPath
):
>>> Path('setup.py')
PosixPath('setup.py')
pathsegments 参数的指定和 PurePath
相同。
class pathlib.PosixPath
(\pathsegments*)
一个 Path
和 PurePosixPath
的子类,此类表示一个非 Windows 文件系统的具体路径:
>>> PosixPath('/etc')
PosixPath('/etc')
pathsegments 参数的指定和 PurePath
相同。
class pathlib.WindowsPath
(\pathsegments*)
Path
和 PureWindowsPath
的子类,从类表示一个 Windows 文件系统的具体路径:
>>> WindowsPath('c:/Program Files/')
WindowsPath('c:/Program Files')
pathsegments 参数的指定和 PurePath
相同。
你只能实例化与当前系统风格相同的类(允许系统调用作用于不兼容的路径风格可能在应用程序中导致缺陷或失败):
>>> import os
>>> os.name
'posix'
>>> Path('setup.py')
PosixPath('setup.py')
>>> PosixPath('setup.py')
PosixPath('setup.py')
>>> WindowsPath('setup.py')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "pathlib.py", line 798, in __new__
% (cls.__name__,))
NotImplementedError: cannot instantiate 'WindowsPath' on your system
方法
除纯路径方法外,实体路径还提供以下方法。 如果系统调用失败(例如因为路径不存在)这些方法中许多都会引发 OSError
。
在 3.8 版更改: 对于包含 OS 层级无法表示字符的路径,exists()
, is_dir()
, is_file()
, is_mount()
, is_symlink()
, is_block_device()
, is_char_device()
, is_fifo()
, is_socket()
现在将返回 False
而不是引发异常。
classmethod Path.cwd
()
返回一个新的表示当前目录的路径对象(和 os.getcwd()
返回的相同):
>>> Path.cwd()
PosixPath('/home/antoine/pathlib')
classmethod Path.home
()
返回一个表示用户家目录的新路径对象(与带 ~
构造的 os.path.expanduser()
所返回的相同)。 如果无法解析家目录,则会引发 RuntimeError
。
>>> Path.home()
PosixPath('/home/antoine')
3.5 新版功能.
Path.stat
(**, follow_symlinks=True*)
返回一个 os.stat_result
对象,其中包含有关此路径的信息,例如 os.stat()
。 结果会在每次调用此方法时重新搜索。
此方法通常会跟随符号链接;要对 symlink 使用 stat 请添加参数 follow_symlinks=False
,或者使用 lstat()
。
>>> p = Path('setup.py')
>>> p.stat().st_size
956
>>> p.stat().st_mtime
1327883547.852554
在 3.10 版更改: 增加了 follow_symlinks 形参。
Path.chmod
(mode, **, follow_symlinks=True*)
改变文件模式和权限,和 os.chmod()
一样。
此方法通常会跟随符号链接。 某些 Unix 变种支持改变 symlink 本身的权限;在这些平台上你可以添加参数 follow_symlinks=False
,或者使用 lchmod()
。
>>> p = Path('setup.py')
>>> p.stat().st_mode
33277
>>> p.chmod(0o444)
>>> p.stat().st_mode
33060
在 3.10 版更改: 增加了 follow_symlinks 形参。
Path.exists
()
此路径是否指向一个已存在的文件或目录:
>>> Path('.').exists()
True
>>> Path('setup.py').exists()
True
>>> Path('/etc').exists()
True
>>> Path('nonexistentfile').exists()
False
注解
如果路径指向一个符号链接, exists()
返回此符号链接是否指向存在的文件或目录。
Path.expanduser
()
返回带有扩展 ~
和 ~user
构造的新路径,与 os.path.expanduser()
所返回的相同。 如果无法解析家目录,则会引发 RuntimeError
。
>>> p = PosixPath('~/films/Monty Python')
>>> p.expanduser()
PosixPath('/home/eric/films/Monty Python')
3.5 新版功能.
Path.glob
(pattern)
解析相对于此路径的通配符 pattern,产生所有匹配的文件:
>>> sorted(Path('.').glob('*.py'))
[PosixPath('pathlib.py'), PosixPath('setup.py'), PosixPath('test_pathlib.py')]
>>> sorted(Path('.').glob('*/*.py'))
[PosixPath('docs/conf.py')]
pattern 的形式与 fnmatch
的相同,还增加了 “**
“ 表示 “此目录以及所有子目录,递归”。 换句话说,它启用递归通配:
>>> sorted(Path('.').glob('**/*.py'))
[PosixPath('build/lib/pathlib.py'),
PosixPath('docs/conf.py'),
PosixPath('pathlib.py'),
PosixPath('setup.py'),
PosixPath('test_pathlib.py')]
注解
在一个较大的目录树中使用 “**
“ 模式可能会消耗非常多的时间。
引发一个 审计事件 pathlib.Path.glob
附带参数 self
, pattern
。
Path.group
()
返回拥有此文件的用户组。如果文件的 GID 无法在系统数据库中找到,将抛出 KeyError
。
Path.is_dir
()
如果路径指向一个目录(或者一个指向目录的符号链接)则返回 True
,如果指向其他类型的文件则返回 False
。
当路径不存在或者是一个破损的符号链接时也会返回 False
;其他错误(例如权限错误)被传播。
Path.is_file
()
如果路径指向一个正常的文件(或者一个指向正常文件的符号链接)则返回 True
,如果指向其他类型的文件则返回 False
。
当路径不存在或者是一个破损的符号链接时也会返回 False
;其他错误(例如权限错误)被传播。
Path.is_mount
()
如果路径是一个 挂载点 <mount point>*:在文件系统中被其他不同的文件系统挂载的地点。在 POSIX 系统,此函数检查 *path 的父级 —— path/..
是否处于一个和 path 不同的设备中,或者 file:path/.. 和 path 是否指向相同设备的相同 i-node —— 这能检测所有 Unix 以及 POSIX 变种上的挂载点。 Windows 上未实现。
3.7 新版功能.
Path.is_symlink
()
如果路径指向符号链接则返回 True
, 否则 False
。
如果路径不存在也返回 False
;其他错误(例如权限错误)被传播。
Path.is_socket
()
如果路径指向一个 Unix socket 文件(或者指向 Unix socket 文件的符号链接)则返回 True
,如果指向其他类型的文件则返回 False
。
当路径不存在或者是一个破损的符号链接时也会返回 False
;其他错误(例如权限错误)被传播。
Path.is_fifo
()
如果路径指向一个先进先出存储(或者指向先进先出存储的符号链接)则返回 True
,指向其他类型的文件则返回 False
。
当路径不存在或者是一个破损的符号链接时也会返回 False
;其他错误(例如权限错误)被传播。
Path.is_block_device
()
如果文件指向一个块设备(或者指向块设备的符号链接)则返回 True
,指向其他类型的文件则返回 False
。
当路径不存在或者是一个破损的符号链接时也会返回 False
;其他错误(例如权限错误)被传播。
Path.is_char_device
()
如果路径指向一个字符设备(或指向字符设备的符号链接)则返回 True
,指向其他类型的文件则返回 False
。
当路径不存在或者是一个破损的符号链接时也会返回 False
;其他错误(例如权限错误)被传播。
Path.iterdir
()
当路径指向一个目录时,产生该路径下的对象的路径:
>>> p = Path('docs')
>>> for child in p.iterdir(): child
...
PosixPath('docs/conf.py')
PosixPath('docs/_templates')
PosixPath('docs/make.bat')
PosixPath('docs/index.rst')
PosixPath('docs/_build')
PosixPath('docs/_static')
PosixPath('docs/Makefile')
子条目会以任意顺序生成,并且不包括特殊条目 '.'
和 '..'
。 如果有文件在迭代器创建之后在目录中被移除或添加,是否要包括该文件对应的路径对象并没有规定。
Path.lchmod
(mode)
就像 Path.chmod()
但是如果路径指向符号链接则是修改符号链接的模式,而不是修改符号链接的目标。
Path.lstat
()
就和 Path.stat()
一样,但是如果路径指向符号链接,则是返回符号链接而不是目标的信息。
Path.mkdir
(mode=511, parents=False, exist_ok=False)
新建给定路径的目录。如果给出了 mode ,它将与当前进程的 umask
值合并来决定文件模式和访问标志。如果路径已经存在,则抛出 FileExistsError
。
如果 parents 为 true,任何找不到的父目录都会伴随着此路径被创建;它们会以默认权限被创建,而不考虑 mode 设置(模仿 POSIX 的 mkdir -p
命令)。
如果 parents 为 false(默认),则找不到的父级目录会导致 FileNotFoundError
被抛出。
如果 exist_ok 为 false(默认),则在目标已存在的情况下抛出 FileExistsError
。
如果 exist_ok 为 true, 则 FileExistsError
异常将被忽略(和 POSIX mkdir -p
命令行为相同),但是只有在最后一个路径组件不是现存的非目录文件时才生效。
在 3.5 版更改: exist_ok 形参被加入。
Path.open
(mode=’r’, buffering=- 1, encoding=None, errors=None, newline=None)
打开路径指向的文件,就像内置的 open()
函数所做的一样:
>>> p = Path('setup.py')
>>> with p.open() as f:
... f.readline()
...
'#!/usr/bin/env python3\n'
Path.owner
()
返回拥有此文件的用户名。如果文件的 UID 无法在系统数据库中找到,则抛出 KeyError
。
Path.read_bytes
()
以字节对象的形式返回路径指向的文件的二进制内容:
>>> p = Path('my_binary_file')
>>> p.write_bytes(b'Binary file contents')
20
>>> p.read_bytes()
b'Binary file contents'
3.5 新版功能.
Path.read_text
(encoding=None, errors=None)
以字符串形式返回路径指向的文件的解码后文本内容。
>>> p = Path('my_text_file')
>>> p.write_text('Text file contents')
18
>>> p.read_text()
'Text file contents'
文件先被打开然后关闭。有和 open()
一样的可选形参。
3.5 新版功能.
Path.readlink
()
返回符号链接所指向的路径(即 os.readlink()
的返回值):
>>> p = Path('mylink')
>>> p.symlink_to('setup.py')
>>> p.readlink()
PosixPath('setup.py')
3.9 新版功能.
Path.rename
(target)
将文件或目录重命名为给定的 target*,并返回一个新的指向 *target 的 Path 实例。 在 Unix 上,如果 target 存在且为一个文件,如果用户有足够权限,则它将被静默地替换。 target 可以是一个字符串或者另一个路径对象:
>>> p = Path('foo')
>>> p.open('w').write('some text')
9
>>> target = Path('bar')
>>> p.rename(target)
PosixPath('bar')
>>> target.open().read()
'some text'
目标路径可能为绝对或相对路径。 相对路径将被解释为相对于当前工作目录,而 不是 相对于 Path 对象的目录。
在 3.8 版更改: 添加了返回值,返回新的 Path 实例。
Path.replace
(target)
将文件名目录重命名为给定的 target*,并返回一个新的指向 *target 的 Path 实例。 如果 target 指向一个现有文件或目录,则它将被无条件地替换。
目标路径可能为绝对或相对路径。 相对路径将被解释为相对于当前工作目录,而 不是 相对于 Path 对象的目录。
在 3.8 版更改: 添加了返回值,返回新的 Path 实例。
Path.resolve
(strict=False)
将路径绝对化,解析任何符号链接。返回新的路径对象:
>>> p = Path()
>>> p
PosixPath('.')
>>> p.resolve()
PosixPath('/home/antoine/pathlib')
“..
“ 组件也将被消除(只有这一种方法这么做):
>>> p = Path('docs/../setup.py')
>>> p.resolve()
PosixPath('/home/antoine/pathlib/setup.py')
如果路径不存在并且 strict 设为 True
,则抛出 FileNotFoundError
。如果 strict 为 False
,则路径将被尽可能地解析并且任何剩余部分都会被不检查是否存在地追加。如果在解析路径上发生无限循环,则抛出 RuntimeError
。
3.6 新版功能: 加入strict 参数(3.6之前的版本相当于strict值为True)
Path.rglob
(pattern)
这就像调用 Path.glob
时在给定的相对 pattern 前面添加了”``**/()
“
>>> sorted(Path().rglob("*.py"))
[PosixPath('build/lib/pathlib.py'),
PosixPath('docs/conf.py'),
PosixPath('pathlib.py'),
PosixPath('setup.py'),
PosixPath('test_pathlib.py')]
引发一个 审计事件 pathlib.Path.rglob
附带参数 self
, pattern
。
Path.rmdir
()
移除此目录。此目录必须为空的。
Path.samefile
(other_path)
返回此目录是否指向与可能是字符串或者另一个路径对象的 other_path 相同的文件。语义类似于 os.path.samefile()
与 os.path.samestat()
。
如果两者都以同一原因无法访问,则抛出 OSError
。
>>> p = Path('spam')
>>> q = Path('eggs')
>>> p.samefile(q)
False
>>> p.samefile('spam')
True
3.5 新版功能.
Path.symlink_to
(target, target_is_directory=False)
将此路径创建为指向 target 的符号链接。在 Windows 下,如果链接的目标是一个目录则 target_is_directory 必须为 true (默认为 False
)。在 POSIX 下, target_is_directory 的值将被忽略。
>>> p = Path('mylink')
>>> p.symlink_to('setup.py')
>>> p.resolve()
PosixPath('/home/antoine/pathlib/setup.py')
>>> p.stat().st_size
956
>>> p.lstat().st_size
8
注解
参数的顺序(link, target) 和 os.symlink()
是相反的。
Path.hardlink_to
(target)
将此路径设为一个指向与 target 相同文件的硬链接。
注解
参数顺序 (link, target) 和 os.link()
是相反的。
3.10 新版功能.
Path.link_to
(target)
创建硬链接 target 指向此路径。
警告
此函数不会将此路径设为指向 target 的硬链接,尽管此函数名和参数名有此含义。 参数顺序 (target, link) 与 Path.symlink_to()
和 Path.hardlink_to()
相反,而与 os.link()
的一致。
3.8 新版功能.
3.10 版后已移除: 此方法已被弃用而建议改用 Path.hardlink_to()
,因为 Path.link_to()
的参数顺序与 Path.symlink_to()
的不相匹配。
Path.touch
(mode=438, exist_ok=True)
将给定的路径创建为文件。如果给出了 mode 它将与当前进程的 umask
值合并以确定文件的模式和访问标志。如果文件已经存在,则当 exist_ok 为 true 则函数仍会成功(并且将它的修改事件更新为当前事件),否则抛出 FileExistsError
。
Path.unlink
(missing_ok=False)
移除此文件或符号链接。如果路径指向目录,则用 Path.rmdir()
代替。
如果 missing_ok 为假值(默认),则如果路径不存在将会引发 FileNotFoundError
。
如果 missing_ok 为真值,则 FileNotFoundError
异常将被忽略(和 POSIX rm -f
命令的行为相同)。
在 3.8 版更改: 增加了 missing_ok 形参。
Path.write_bytes
(data)
将文件以二进制模式打开,写入 data 并关闭:
>>> p = Path('my_binary_file')
>>> p.write_bytes(b'Binary file contents')
20
>>> p.read_bytes()
b'Binary file contents'
一个同名的现存文件将被覆盖。
3.5 新版功能.
Path.write_text
(data, encoding=None, errors=None, newline=None)
将文件以文本模式打开,写入 data 并关闭:
>>> p = Path('my_text_file')
>>> p.write_text('Text file contents')
18
>>> p.read_text()
'Text file contents'
同名的现有文件会被覆盖。 可选形参的含义与 open()
的相同。
3.5 新版功能.
在 3.10 版更改: 增加了 newline 形参。
对应的 os
模块的工具
以下是一个映射了 os
与 PurePath
/Path
对应相同的函数的表。
注解
以下函数/方法对并不完全等价。 它们有些虽然具有相互重叠的使用场景,但语义并不相同。 这包括了 os.path.abspath()
和 Path.resolve()
,以及 os.path.relpath()
和 PurePath.relative_to()
。
os 和 os.path |
pathlib |
---|---|
os.path.abspath() |
Path.resolve() |
os.chmod() |
Path.chmod() |
os.mkdir() |
Path.mkdir() |
os.makedirs() |
Path.mkdir() |
os.rename() |
Path.rename() |
os.replace() |
Path.replace() |
os.rmdir() |
Path.rmdir() |
os.remove() , os.unlink() |
Path.unlink() |
os.getcwd() |
Path.cwd() |
os.path.exists() |
Path.exists() |
os.path.expanduser() |
Path.expanduser() 和 Path.home() |
os.listdir() |
Path.iterdir() |
os.path.isdir() |
Path.is_dir() |
os.path.isfile() |
Path.is_file() |
os.path.islink() |
Path.is_symlink() |
os.link() |
Path.hardlink_to() |
os.symlink() |
Path.symlink_to() |
os.readlink() |
Path.readlink() |
os.path.relpath() |
Path.relative_to() |
os.stat() |
Path.stat() , Path.owner() , Path.group() |
os.path.isabs() |
PurePath.is_absolute() |
os.path.join() |
PurePath.joinpath() |
os.path.basename() |
PurePath.name |
os.path.dirname() |
PurePath.parent |
os.path.samefile() |
Path.samefile() |
os.path.splitext() |
PurePath.suffix |
os.path
—- 常用路径操作
源代码: Lib/posixpath.py (用于 POSIX)和 Lib/ntpath.py (用于 Windows NT)
该模块在路径名上实现了一些有用的功能。路径参数可以字符串或字节形式传递。我们鼓励应用程序将文件名表示为(Unicode)字符串。不幸的是,某些文件名在Unix上可能无法用字符串表示,因此在Unix上平台上需要支持任意文件名的应用程序,应使用字节对象来表示路径名。反之亦然,在Windows平台上仅使用字节对象,不能表示的所有文件名(以标准 mbcs
编码),因此Windows应用程序应使用字符串对象来访问所有文件。
与unix shell不同,Python不执行任何 自动 路径扩展。当应用程序需要类似shell的路径扩展时,可以显式调用诸如 expanduser()
和 expandvars()
之类的函数。
参见
pathlib
模块提供高级路径对象。
注解
所有这些函数都仅接受字节或字符串对象作为其参数。如果返回路径或文件名,则结果是相同类型的对象。
注解
由于不同的操作系统具有不同的路径名称约定,因此标准库中有此模块的几个版本。os.path
模块始终是适合 Python 运行的操作系统的路径模块,因此可用于本地路径。但是,如果操作的路径 总是 以一种不同的格式显示,那么也可以分别导入和使用各个模块。它们都具有相同的接口:
posixpath
用于Unix 样式的路径ntpath
用于 Windows 路径
在 3.8 版更改: exists()
、lexists()
、isdir()
、isfile()
、islink()
和 ismount()
现在遇到系统层面上不可表示的字符或字节的路径时,会返回 False
,而不是抛出异常。
os.path.abspath
(path)
返回路径 path 的绝对路径(标准化的)。在大多数平台上,这等同于用 normpath(join(os.getcwd(), path))
的方式调用 normpath()
函数。
在 3.6 版更改: 接受一个 path-like object。
os.path.basename
(path)
返回路径 path 的基本名称。这是将 path 传入函数 split()
之后,返回的一对值中的第二个元素。请注意,此函数的结果与Unix basename 程序不同。basename 在 '/foo/bar/'
上返回 'bar'
,而 basename()
函数返回一个空字符串 (''
)。
在 3.6 版更改: 接受一个 path-like object。
os.path.commonpath
(paths)
接受包含多个路径的序列 paths*,返回 *paths 的最长公共子路径。如果 paths 同时包含绝对路径和相对路径,或 paths 在不同的驱动器上,或 paths 为空,则抛出 ValueError
异常。与 commonprefix()
不同,本方法返回有效路径。
可用性: Unix, Windows。
3.5 新版功能.
在 3.6 版更改: 接受一个 类路径对象 序列。
os.path.commonprefix
(list)
接受包含多个路径的 列表*,返回所有路径的最长公共前缀(逐字符比较)。如果 *列表 为空,则返回空字符串 (''
)。
注解
此函数是逐字符比较,因此可能返回无效路径。要获取有效路径,参见 commonpath()
。
>>> os.path.commonprefix(['/usr/lib', '/usr/local/lib'])
'/usr/l'
>>> os.path.commonpath(['/usr/lib', '/usr/local/lib'])
'/usr'
在 3.6 版更改: 接受一个 path-like object。
os.path.dirname
(path)
返回路径 path 的目录名称。这是将 path 传入函数 split()
之后,返回的一对值中的第一个元素。
在 3.6 版更改: 接受一个 path-like object。
os.path.exists
(path)
如果 path 指向一个已存在的路径或已打开的文件描述符,返回 True
。对于失效的符号链接,返回 False
。在某些平台上,如果使用 os.stat()
查询到目标文件没有执行权限,即使 path 确实存在,本函数也可能返回 False
。
在 3.3 版更改: path 现在可以是一个整数:如果该整数是一个已打开的文件描述符,返回 True
,否则返回 False
。
在 3.6 版更改: 接受一个 path-like object。
os.path.lexists
(path)
如果 path 指向一个已存在的路径,返回 True
。对于失效的符号链接,也返回 True
。在缺失 os.lstat()
的平台上等同于 exists()
。
在 3.6 版更改: 接受一个 path-like object。
os.path.expanduser
(path)
在 Unix 和 Windows 上,将参数中开头部分的 ~
或 ~user
替换为当前 用户 的家目录并返回。
在 Unix 上,开头的 ~
会被环境变量 HOME
代替,如果变量未设置,则通过内置模块 pwd
在 password 目录中查找当前用户的主目录。以 ~user
开头则直接在 password 目录中查找。
在 Windows 上,如果 USERPROFILE
已设置将会被使用,否则 HOMEPATH
和 HOMEDRIVE
将被组合起来使用。 初始的 ~user
会通过检查当前用户的家目录中匹配 USERNAME
的最后一部分目录名并执行替换来处理。
如果展开路径失败,或者路径不是以波浪号开头,则路径将保持不变。
在 3.6 版更改: 接受一个 path-like object。
在 3.8 版更改: Windows 不再使用 HOME
。
os.path.expandvars
(path)
输入带有环境变量的路径作为参数,返回展开变量以后的路径。$name
或 ${name}
形式的子字符串被环境变量 name 的值替换。格式错误的变量名称和对不存在变量的引用保持不变。
在 Windows 上,除了 $name
和 ${name}
外,还可以展开 %name%
。
在 3.6 版更改: 接受一个 path-like object。
os.path.getatime
(path)
返回 path 的最后访问时间。返回值是一个浮点数,为纪元秒数。如果该文件不存在或不可访问,则抛出 OSError
异常。
os.path.getmtime
(path)
返回 path 的最后修改时间。返回值是一个浮点数,为纪元秒数。如果该文件不存在或不可访问,则抛出 OSError
异常。
在 3.6 版更改: 接受一个 path-like object。
os.path.getctime
(path)
返回 path 在系统中的 ctime,在有些系统(比如 Unix)上,它是元数据的最后修改时间,其他系统(比如 Windows)上,它是 path 的创建时间。返回值是一个数,为纪元秒数。如果该文件不存在或不可访问,则抛出 OSError
异常。
在 3.6 版更改: 接受一个 path-like object。
os.path.getsize
(path)
返回 path 的大小,以字节为单位。如果该文件不存在或不可访问,则抛出 OSError
异常。
在 3.6 版更改: 接受一个 path-like object。
os.path.isabs
(path)
如果 path 是一个绝对路径,则返回 True
。在 Unix 上,它就是以斜杠开头,而在 Windows 上,它可以是去掉驱动器号后以斜杠(或反斜杠)开头。
在 3.6 版更改: 接受一个 path-like object。
os.path.isfile
(path)
如果 path 是 现有的
常规文件,则返回 True
。本方法会跟踪符号链接,因此,对于同一路径,islink()
和 isfile()
都可能为 True
。
在 3.6 版更改: 接受一个 path-like object。
os.path.isdir
(path)
如果 path 是 现有的
目录,则返回 True
。本方法会跟踪符号链接,因此,对于同一路径,islink()
和 isdir()
都可能为 True
。
在 3.6 版更改: 接受一个 path-like object。
os.path.islink
(path)
如果 path 指向的 现有
目录条目是一个符号链接,则返回 True
。如果 Python 运行时不支持符号链接,则总是返回 False
。
在 3.6 版更改: 接受一个 path-like object。
os.path.ismount
(path)
如果路径 path 是 挂载点 (文件系统中挂载其他文件系统的点),则返回 True
。在 POSIX 上,该函数检查 path 的父目录 *path*/..
是否在与 path 不同的设备上,或者 *path*/..
和 path 是否指向同一设备上的同一 inode(这一检测挂载点的方法适用于所有 Unix 和 POSIX 变体)。本方法不能可靠地检测同一文件系统上的绑定挂载 (bind mount)。在 Windows 上,盘符和共享 UNC 始终是挂载点,对于任何其他路径,将调用 GetVolumePathName
来查看它是否与输入的路径不同。
3.4 新版功能: 支持在 Windows 上检测非根挂载点。
在 3.6 版更改: 接受一个 path-like object。
os.path.join
(path, \paths*)
智能地拼接一个或多个路径部分。 返回值是 path 和 \paths* 的所有成员的拼接,其中每个非空部分后面都紧跟一个目录分隔符,最后一个部分除外,这意味着如果最后一个部分为空,则结果将以分隔符结尾。 如果某个部分为绝对路径,则之前的所有部分会被丢弃并从绝对路径部分开始继续拼接。
在 Windows 上,遇到绝对路径部分(例如 r'\foo'
)时,不会重置盘符。如果某部分路径包含盘符,则会丢弃所有先前的部分,并重置盘符。请注意,由于每个驱动器都有一个“当前目录”,所以 os.path.join("c:", "foo")
表示驱动器 C:
上当前目录的相对路径 (c:foo
),而不是 c:\foo
。
在 3.6 版更改: 接受一个 类路径对象 用于 path 和 paths 。
os.path.normcase
(path)
规范路径的大小写。在 Windows 上,将路径中的所有字符都转换为小写,并将正斜杠转换为反斜杠。在其他操作系统上返回原路径。
在 3.6 版更改: 接受一个 path-like object。
os.path.normpath
(path)
通过折叠多余的分隔符和对上级目录的引用来标准化路径名,所以
A//B
、A/B/
、A/./B
和A/foo/../B
都会转换成A/B
。这个字符串操作可能会改变带有符号链接的路径的含义。在 Windows 上,本方法将正斜杠转换为反斜杠。要规范大小写,请使用normcase()
。
注解
在 POSIX 系统上,根据 IEEE Std 1003.1 2013 Edition; 4.13 Pathname Resolution,如果一个路径名称以两个斜杠开始,则开始字符之后的第一个部分将以具体实现所定义的方式来解读,但是超过两个开始字符则将被视为单个字符。
在 3.6 版更改: 接受一个 path-like object。
os.path.realpath
(path, **, strict=False*)
返回指定文件的规范路径,消除路径中存在的任何符号链接(如果操作系统支持)。
如果一个路径不存在或是遇到了符号链接循环,并且 strict 为 True
,则会引发 OSError
。 如果 strict 为 False
,则会尽可能地解析路径并添加结果而不检查路径是否存在。
注解
这个函数会模拟操作系统生成规范路径的过程,Windows 与 UNIX 的这个过程在处理链接和后续路径组成部分的交互方式上有所差异。
操作系统 API 会根据需要来规范化路径,因此通常不需要调用此函数。
在 3.6 版更改: 接受一个 path-like object。
在 3.8 版更改: 在 Windows 上现在可以正确解析符号链接和交接点 (junction point)。
在 3.10 版更改: 增加了 strict 形参。
os.path.relpath
(path, start=os.curdir)
返回从当前目录或可选的 start 目录至 path 的相对文件路径。 这只是一个路径计算:不会访问文件系统来确认 path 或 start 是否存在或其性质。 在 Windows 上,当 path 和 start 位于不同驱动器时将引发 ValueError
。
start 默认为 os.curdir
。
可用性: Unix, Windows。
在 3.6 版更改: 接受一个 path-like object。
os.path.samefile
(path1, path2)
如果两个路径都指向相同的文件或目录,则返回 True
。这由设备号和 inode 号确定,在任一路径上调用 os.stat()
失败则抛出异常。
可用性: Unix, Windows。
在 3.2 版更改: 添加了对 Windows 的支持。
在 3.4 版更改: Windows现在使用与其他所有平台相同的实现。
在 3.6 版更改: 接受一个 path-like object。
os.path.sameopenfile
(fp1, fp2)
如果文件描述符 fp1 和 fp2 指向相同文件,则返回 True
。
可用性: Unix, Windows。
在 3.2 版更改: 添加了对 Windows 的支持。
在 3.6 版更改: 接受一个 path-like object。
os.path.samestat
(stat1, stat2)
如果 stat 元组 stat1 和 stat2 指向相同文件,则返回 True
。这些 stat 元组可能是由 os.fstat()
、os.lstat()
或 os.stat()
返回的。本函数实现了 samefile()
和 sameopenfile()
底层所使用的比较过程。
可用性: Unix, Windows。
在 3.4 版更改: 添加了对 Windows 的支持。
在 3.6 版更改: 接受一个 path-like object。
os.path.split
(path)
将路径 path 拆分为一对,即 (head, tail)
,其中,tail 是路径的最后一部分,而 head 里是除最后部分外的所有内容。tail 部分不会包含斜杠,如果 path 以斜杠结尾,则 tail 将为空。如果 path 中没有斜杠,head 将为空。如果 path 为空,则 head 和 tail 均为空。head 末尾的斜杠会被去掉,除非它是根目录(即它仅包含一个或多个斜杠)。在所有情况下,join(head, tail)
指向的位置都与 path 相同(但字符串可能不同)。另请参见函数 dirname()
和 basename()
。
在 3.6 版更改: 接受一个 path-like object。
os.path.splitdrive
(path)
将路径 path 拆分为一对,即 (drive, tail)
,其中 drive 是挂载点或空字符串。在没有驱动器概念的系统上,drive 将始终为空字符串。在所有情况下,drive + tail
都与 path 相同。
在 Windows 上,本方法将路径拆分为驱动器/UNC 根节点和相对路径。
如果路径 path 包含盘符,则 drive 将包含冒号之前的所有内容包括冒号本身:
>>> splitdrive("c:/dir")
("c:", "/dir")
如果路径 path 包含 UNC 路径,则 drive 将包含主机名和 share,直至第四个分隔符但不包括该分隔符:
>>> splitdrive("//host/computer/dir")
("//host/computer", "/dir")
在 3.6 版更改: 接受一个 path-like object。
os.path.splitext
(path)
将路径名称 path 拆分为 (root, ext)
对使得 root + ext == path
,并且扩展名 ext 为空或以句点打头并最多只包含一个句点。
如果路径 path 不包含扩展名,则 ext 将为 ''
:
>>> splitext('bar')
('bar', '')
如果路径 path 包含扩展名,则 ext 将被设为该扩展名,包括打头的句点。 请注意在其之前的句点将被忽略:
>>> splitext('foo.bar.exe')
('foo.bar', '.exe')
基本名中打头的句点会被忽略:
>>> splitext('.cshrc')
('.cshrc', '')
在 3.6 版更改: 接受一个 path-like object。
os.path.supports_unicode_filenames
fileinput
—- 迭代来自多个输入流的行
源代码: Lib/fileinput.py
此模块实现了一个辅助类和一些函数用来快速编写访问标准输入或文件列表的循环。
典型用法为:
import fileinput
for line in fileinput.input(encoding="utf-8"):
process(line)
此程序会迭代 sys.argv[1:]
中列出的所有文件内的行,如果列表为空则会使用 sys.stdin
。 如果有一个文件名为 '-'
,它也会被替换为 sys.stdin
并且可选参数 mode 和 openhook 会被忽略。 要指定替代文件列表,请将其作为第一个参数传给 input()
。 也允许使用单个文件。
所有文件都默认以文本模式打开,但你可以通过在调用 input()
或 FileInput
时指定 mode 形参来重载此行为。 如果在打开或读取文件时发生了 I/O 错误,将会引发 OSError
。
在 3.3 版更改: 原来会引发 IOError
;现在它是 OSError
的别名。
如果 sys.stdin
被使用超过一次,则第二次之后的使用将不返回任何行,除非是被交互式的使用,或都是被显式地重置 (例如使用 sys.stdin.seek(0)
)。
空文件打开后将立即被关闭;它们在文件列表中会被注意到的唯一情况只有当最后打开的文件为空的时候。
反回的行不会对换行符做任何处理,这意味着文件中的最后一行可能不带换行符。
You can control how files are opened by providing an opening hook via the openhook parameter to fileinput.input()
or FileInput()
. The hook must be a function that takes two arguments, filename and mode, and returns an accordingly opened file-like object. If encoding and/or errors are specified, they will be passed to the hook as aditional keyword arguments. This module provides a hook_compressed()
to support compressed files.
以下函数是此模块的初始接口:
fileinput.input
(files=None, inplace=False, backup=’’, **, mode=’r’, openhook=None, encoding=None, errors=None*)
创建一个 FileInput
类的实例。 该实例将被用作此模块中函数的全局状态,并且还将在迭代期间被返回使用。 此函数的形参将被继续传递给 FileInput
类的构造器。
FileInput
实例可以在 with
语句中被用作上下文管理器。 在这个例子中,input 在 with
语句结束后将会被关闭,即使发生了异常也是如此:
with fileinput.input(files=('spam.txt', 'eggs.txt'), encoding="utf-8") as f:
for line in f:
process(line)
在 3.2 版更改: 可以被用作上下文管理器。
在 3.8 版更改: 关键字形参 mode 和 openhook 现在是仅限关键字形参。
在 3.10 版更改: 增加了仅限关键字形参 encoding 和 errors。
下列函数会使用 fileinput.input()
所创建的全局状态;如果没有活动的状态,则会引发 RuntimeError
。
fileinput.filename
()
返回当前被读取的文件名。 在第一行被读取之前,返回 None
。
fileinput.fileno
()
返回以整数表示的当前文件“文件描述符”。 当未打开文件时(处在第一行和文件之间),返回 -1
。
fileinput.lineno
()
返回已被读取的累计行号。 在第一行被读取之前,返回 0
。 在最后一个文件的最后一行被读取之后,返回该行的行号。
fileinput.filelineno
()
返回当前文件中的行号。 在第一行被读取之前,返回 0
。 在最后一个文件的最后一行被读取之后,返回此文件中该行的行号。
fileinput.isfirstline
()
如果刚读取的行是其所在文件的第一行则返回 True
,否则返回 False
。
fileinput.isstdin
()
如果最后读取的行来自 sys.stdin
则返回 True
,否则返回 False
。
fileinput.nextfile
()
关闭当前文件以使下次迭代将从下一个文件(如果存在)读取第一行;不是从该文件读取的行将不会被计入累计行数。 直到下一个文件的第一行被读取之后文件名才会改变。 在第一行被读取之前,此函数将不会生效;它不能被用来跳过第一个文件。 在最后一个文件的最后一行被读取之后,此函数将不再生效。
fileinput.close
()
关闭序列。
此模块所提供的实现了序列行为的类同样也可用于子类化:
class fileinput.FileInput
(files=None, inplace=False, backup=’’, **, mode=’r’, openhook=None, encoding=None, errors=None*)
类 FileInput
是一个实现;它的方法 filename()
, fileno()
, lineno()
, filelineno()
, isfirstline()
, isstdin()
, nextfile()
和 close()
对应于此模块中具有相同名称的函数。 此外它还有一个 readline()
方法可返回下一个输入行,以及一个 __getitem__()
方法,该方法实现了序列行为。 这种序列必须以严格的序列顺序来读写;随机读写和 readline()
不可以被混用。
通过 mode 你可以指定要传给 open()
的文件模式。 它必须为 'r'
, 'rU'
, 'U'
和 'rb'
中的一个。
openhook 如果给出则必须为一个函数,它接受两个参数 filename 和 mode*,并相应地返回一个打开的文件类对象。 你不能同时使用 *inplace 和 openhook。
你可以指定 encoding 和 errors 来将其传给 open()
或 openhook。
FileInput
实例可以在 with
语句中被用作上下文管理器。 在这个例子中,input 在 with
语句结束后将会被关闭,即使发生了异常也是如此:
with FileInput(files=('spam.txt', 'eggs.txt')) as input:
process(input)
在 3.2 版更改: 可以被用作上下文管理器。
3.4 版后已移除: 'rU'
和 'U'
模式。
3.8 版后已移除: 对 __getitem__()
方法的支持已弃用。
在 3.8 版更改: 关键字形参 mode 和 openhook 现在是仅限关键字形参。
在 3.10 版更改: 增加了仅限关键字形参 encoding 和 errors。
可选的原地过滤: 如果传递了关键字参数 inplace=True
给 fileinput.input()
或 FileInput
构造器,则文件会被移至备份文件并将标准输出定向到输入文件(如果已存在与备份文件同名的文件,它将被静默地替换)。 这使得编写一个能够原地重写其输入文件的过滤器成为可能。 如果给出了 backup 形参 (通常形式为 backup='.<some extension>'
),它将指定备份文件的扩展名,并且备份文件会被保留;默认情况下扩展名为 '.bak'
并且它会在输出文件关闭时被删除。 在读取标准输入时原地过滤会被禁用。
此模块提供了以下两种打开文件钩子:
fileinput.hook_compressed
(filename, mode, **, encoding=None, errors=None*)
使用 gzip
和 bz2
模块透明地打开 gzip 和 bzip2 压缩的文件(通过扩展名 '.gz'
和 '.bz2'
来识别)。 如果文件扩展名不是 '.gz'
或 '.bz2'
,文件会以正常方式打开(即使用 open()
并且不带任何解压操作)。
encoding 和 errors 值会被传给 io.TextIOWrapper
用于压缩文件以及打开普通文件。
用法示例: fi = fileinput.FileInput(openhook=fileinput.hook_compressed, encoding="utf-8")
在 3.10 版更改: 增加了仅限关键字形参 encoding 和 errors。
fileinput.hook_encoded
(encoding, errors=None)
返回一个通过 open()
打开每个文件的钩子,使用给定的 encoding 和 errors 来读取文件。
使用示例: fi = fileinput.FileInput(openhook=fileinput.hook_encoded("utf-8", "surrogateescape"))
在 3.6 版更改: 添加了可选的 errors 形参。
3.10 版后已移除: 此函数已被弃用,因为 input()
和 FileInput
现在有了 encoding 和 errors 形参。
stat
—- 解析 stat()
结果
源代码: Lib/stat.py
stat
模块定义了一些用于解析 os.stat()
, os.fstat()
和 os.lstat()
(如果它们存在) 输出结果的常量和函数。 有关 stat()
, fstat()
和 lstat()
调用的完整细节,请参阅你的系统文档。
在 3.4 版更改: stat 模块是通过 C 实现来支持的。
stat
模块定义了以下函数来检测特定文件类型:
stat.S_ISDIR
(mode)
如果 mode 来自一个目录则返回非零值。
stat.S_ISCHR
(mode)
如果 mode 来自一个字符特殊设备文件则返回非零值。
stat.S_ISBLK
(mode)
如果 mode 来自一个块特殊设备文件则返回非零值。
stat.S_ISREG
(mode)
如果 mode 来自一个常规文件则返回非零值。
stat.S_ISFIFO
(mode)
如果 mode 来自一个 FIFO (命名管道) 则返回非零值。
stat.S_ISLNK
(mode)
如果 mode 来自一个符号链接则返回非零值。
stat.S_ISSOCK
(mode)
如果 mode 来自一个套接字则返回非零值。
stat.S_ISDOOR
(mode)
如果 mode 来自一个门则返回非零值。
3.4 新版功能.
stat.S_ISPORT
(mode)
如果 mode 来自一个事件端口则返回非零值。
3.4 新版功能.
stat.S_ISWHT
(mode)
如果 mode 来自一个白输出则返回非零值。
3.4 新版功能.
定义了两个附加函数用于对文件模式进行更一般化的操作:
stat.S_IMODE
(mode)
返回文件模式中可由 os.chmod()
进行设置的部分 —- 即文件的 permission 位,加上 sticky 位、set-group-id 以及 set-user-id 位(在支持这些部分的系统上)。
stat.S_IFMT
(mode)
返回文件模式中描述文件类型的部分(供上面的 S_IS*()
函数使用)。
通常,你应当使用 os.path.is*()
函数来检测文件的类型;这里提供的函数则适用于当你要对同一文件执行多项检测并且希望避免每项检测的 stat()
系统调用开销的情况。 这些函数也适用于检测有关未被 os.path
处理的信息,例如检测块和字符设备等。
示例:
import os, sys
from stat import *
def walktree(top, callback):
'''recursively descend the directory tree rooted at top,
calling the callback function for each regular file'''
for f in os.listdir(top):
pathname = os.path.join(top, f)
mode = os.stat(pathname).st_mode
if S_ISDIR(mode):
# It's a directory, recurse into it
walktree(pathname, callback)
elif S_ISREG(mode):
# It's a file, call the callback function
callback(pathname)
else:
# Unknown file type, print a message
print('Skipping %s' % pathname)
def visitfile(file):
print('visiting', file)
if __name__ == '__main__':
walktree(sys.argv[1], visitfile)
另外还提供了一个附加的辅助函数用来将文件模式转换为人类易读的字符串:
stat.filemode
(mode)
将文件模式转换为 ‘-rwxrwxrwx’ 形式的字符串。
3.3 新版功能.
在 3.4 版更改: 此函数支持 S_IFDOOR
, S_IFPORT
and S_IFWHT
。
以下所有变量是一些简单的符号索引,用于访问 os.stat()
, os.fstat()
或 os.lstat()
所返回的 10 条目元组。
stat.ST_MODE
inode 保护模式。
stat.ST_INO
Inode 号
stat.ST_DEV
Inode 所在的设备。
stat.ST_NLINK
Inode 拥有的链接数量。
stat.ST_UID
所有者的用户 ID。
stat.ST_GID
所有者的用户组ID。
stat.ST_SIZE
以字节为单位的普通文件大小;对于某些特殊文件则是所等待的数据量。
stat.ST_ATIME
上次访问的时间。
stat.ST_MTIME
上次修改的时间。
stat.ST_CTIME
操作系统所报告的 “ctime”。 在某些系统上(例如 Unix)是元数据的最后修改时间,而在其他系统上(例如 Windows)则是创建时间(请参阅系统平台的文档了解相关细节)。
对于“文件大小”的解析可因文件类型的不同而变化。 对于普通文件就是文件的字节数。 对于大部分种类的 Unix(特别包括 Linux)的 FIFO 和套接字来说,“大小”则是指在调用 os.stat()
, os.fstat()
或 os.lstat()
时等待读取的字节数;这在某些时候很有用处,特别是在一个非阻塞的打开后轮询这些特殊文件中的一个时。 其他字符和块设备的文件大小字段的含义还会有更多变化,具体取决于底层系统调用的实现方式。
以下变量定义了在 ST_MODE
字段中使用的旗标。
使用上面的函数会比使用第一组旗标更容易移植:
stat.S_IFSOCK
套接字。
stat.S_IFLNK
符号链接。
stat.S_IFREG
普通文件。
stat.S_IFBLK
块设备。
stat.S_IFDIR
目录。
stat.S_IFCHR
字符设备。
stat.S_IFIFO
先进先出。
stat.S_IFDOOR
门。
3.4 新版功能.
stat.S_IFPORT
事件端口。
3.4 新版功能.
stat.S_IFWHT
白输出。
3.4 新版功能.
注解
S_IFDOOR
, S_IFPORT
or S_IFWHT
等文件类型在不受系统平台支持时会被定义为 0。
以下旗标还可以 os.chmod()
的在 mode 参数中使用:
stat.S_ISUID
设置 UID 位。
stat.S_ISGID
设置分组 ID 位。 这个位有几种特殊用途。 对于目录它表示该目录将使用 BSD 语义:在其中创建的文件将从目录继承其分组 ID,而不是从创建进程的有效分组 ID 继承,并且在其中创建的目录也将设置 S_ISGID
位。 对于没有设置分组执行位 (S_IXGRP
) 的文件,设置分组 ID 位表示强制性文件/记录锁定。
stat.S_ISVTX
固定位。 当对目录设置该位时则意味着此目录中的文件只能由文件所有者、目录所有者或特权进程来重命名或删除。
stat.S_IRWXU
文件所有者权限的掩码。
stat.S_IRUSR
所有者具有读取权限。
stat.S_IWUSR
所有者具有写入权限。
stat.S_IXUSR
所有者具有执行权限。
stat.S_IRWXG
组权限的掩码。
stat.S_IRGRP
组具有读取权限。
stat.S_IWGRP
组具有写入权限。
stat.S_IXGRP
组具有执行权限。
stat.S_IRWXO
其他人(不在组中)的权限掩码。
stat.S_IROTH
其他人具有读取权限。
stat.S_IWOTH
其他人具有写入权限。
stat.S_IXOTH
其他人具有执行权限。
stat.S_ENFMT
System V 执行文件锁定。 此旗标是与 S_ISGID
共享的:文件/记录锁定会针对未设置分组执行位 (S_IXGRP
) 的文件强制执行。
stat.S_IREAD
Unix V7 中 S_IRUSR
的同义词。
stat.S_IWRITE
Unix V7 中 S_IWUSR
的同义词。
stat.S_IEXEC
Unix V7 中 S_IXUSR
的同义词。
以下旗标可以在 os.chflags()
的 flags 参数中使用:
stat.UF_NODUMP
不要转储文件。
stat.UF_IMMUTABLE
文件不能被更改。
stat.UF_APPEND
文件只能被附加。
stat.UF_OPAQUE
当通过联合堆栈查看时,目录是不透明的。
stat.UF_NOUNLINK
文件不能重命名或删除。
stat.UF_COMPRESSED
The file is stored compressed (macOS 10.6+).
stat.UF_HIDDEN
The file should not be displayed in a GUI (macOS 10.5+).
stat.SF_ARCHIVED
文件可能已存档。
stat.SF_IMMUTABLE
文件不能被更改。
stat.SF_APPEND
文件只能被附加。
stat.SF_NOUNLINK
文件不能重命名或删除。
stat.SF_SNAPSHOT
文件有一个快照文件
See the BSD or macOS systems man page *chflags(2) for more information.
在 Windows 上,以下文件属性常量可被用来检测 os.stat()
所返回的 st_file_attributes
成员中的位。 请参阅 Windows API 文档 了解有关这些常量含义的详情。
stat.FILE_ATTRIBUTE_ARCHIVE
stat.FILE_ATTRIBUTE_COMPRESSED
stat.FILE_ATTRIBUTE_DEVICE
stat.FILE_ATTRIBUTE_DIRECTORY
stat.FILE_ATTRIBUTE_ENCRYPTED
stat.FILE_ATTRIBUTE_HIDDEN
stat.FILE_ATTRIBUTE_INTEGRITY_STREAM
stat.FILE_ATTRIBUTE_NORMAL
stat.FILE_ATTRIBUTE_NOT_CONTENT_INDEXED
stat.FILE_ATTRIBUTE_NO_SCRUB_DATA
stat.FILE_ATTRIBUTE_OFFLINE
stat.FILE_ATTRIBUTE_READONLY
stat.FILE_ATTRIBUTE_REPARSE_POINT
stat.FILE_ATTRIBUTE_SPARSE_FILE
stat.FILE_ATTRIBUTE_SYSTEM
stat.FILE_ATTRIBUTE_TEMPORARY
stat.FILE_ATTRIBUTE_VIRTUAL
3.5 新版功能.
在 Windows 上,以下常量可被用来与 os.lstat()
所返回的 st_reparse_tag
成员进行比较。 这些是最主要的常量,而不是详尽的清单。
stat.IO_REPARSE_TAG_SYMLINK
stat.IO_REPARSE_TAG_MOUNT_POINT
stat.IO_REPARSE_TAG_APPEXECLINK
3.8 新版功能.
filecmp
—- 文件及目录的比较
源代码: Lib/filecmp.py
filecmp
模块定义了用于比较文件及目录的函数,并且可以选取多种关于时间和准确性的折衷方案。
filecmp
模块定义了如下函数:
filecmp.cmp
(f1, f2, shallow=True)
比较名为 f1 和 f2 的文件,如果它们似乎相等则返回 True
,否则返回 False
。
如果 shallow 为真值且两个文件的 os.stat()
签名信息(文件类型、大小和修改时间)一致,则文件会被视为相同。
在其他情况下,如果文件大小或内容不同则它们会被视为不同。
需要注意,没有外部程序被该函数调用,这赋予了该函数可移植性与效率。
该函数会缓存过去的比较及其结果,且在文件的 os.stat()
信息变化后缓存条目失效。所有的缓存可以通过使用 clear_cache()
来清除。
filecmp.cmpfiles
(dir1, dir2, common, shallow=True)
比较在两个目录 dir1 和 dir2 中,由 common 所确定名称的文件。
返回三组文件名列表: match, mismatch, errors 。 match 含有相匹配的文件, mismatch 含有那些不匹配的,然后 errors 列出那些未被比较文件的名称。如果文件不存在于两目录中的任一个,或者用户缺少读取它们的权限,又或者因为其他的一些原因而无法比较,那么这些文件将会被列在 errors 中。
参数 shallow 具有同 filecmp.cmp()
一致的含义与默认值。
例如, cmpfiles('a', 'b', ['c', 'd/e'])
将会比较 a/c
与 b/c
以及 a/d/e
与 b/d/e
。 'c'
和 'd/e'
将会各自出现在返回的三个列表里的某一个列表中。
filecmp.clear_cache
()
清除 filecmp 缓存。如果一个文件过快地修改,以至于超过底层文件系统记录修改时间的精度,那么该函数可能有助于比较该类文件。
3.4 新版功能.
dircmp
类
class filecmp.dircmp
(a, b, ignore=None, hide=None)
创建一个用于比较目录 a 和 b 的新的目录比较对象。 ignore 是需要忽略的文件名列表,且默认为 filecmp.DEFAULT_IGNORES
。 hide 是需要隐藏的文件名列表,且默认为 [os.curdir, os.pardir]
。
dircmp
类如 filecmp.cmp()
中所描述的那样对文件进行 shallow 比较。
dircmp
类提供以下方法:
report
()将 a 与 b 之间的比较结果打印(到
sys.stdout
)。report_partial_closure
()打印 a 与 b 及共同直接子目录的比较结果。
report_full_closure
()打印 a 与 b 及共同子目录比较结果(递归地)。
dircmp
类提供了一些有趣的属性,用以得到关于参与比较的目录树的各种信息。
需要注意,通过 __getattr__()
钩子,所有的属性将会惰性求值,因此如果只使用那些计算简便的属性,将不会有速度损失。
left
目录 a 。
right
目录 b 。
left_list
经 hide 和 ignore 过滤,目录 a 中的文件与子目录。
right_list
经 hide 和 ignore 过滤,目录 b 中的文件与子目录。
common
同时存在于目录 a 和 b 中的文件和子目录。
left_only
仅在目录 a 中的文件和子目录。
right_only
仅在目录 b 中的文件和子目录。
common_dirs
同时存在于目录 a 和 b 中的子目录。
common_files
同时存在于目录 a 和 b 中的文件。
common_funny
在目录 a 和 b 中类型不同的名字,或者那些
os.stat()
报告错误的名字。same_files
在目录 a 和 b 中,使用类的文件比较操作符判定相等的文件。
diff_files
在目录 a 和 b 中,根据类的文件比较操作符判定内容不等的文件。
funny_files
在目录 a 和 b 中无法比较的文件。
subdirs
一个将
common_dirs
中的名称映射到dircmp
实例(或者 MyDirCmp 实例,如果该实例类型为dircmp
的子类 MyDirCmp 的话)的字典。在 3.10 版更改: 在之前版本中字典条目总是为
dircmp
实例。 现在条目将与 self 的类型相同,如果 self 为dircmp
的子类的话。
filecmp.DEFAULT_IGNORES
3.4 新版功能.
默认被 dircmp
忽略的目录列表。
下面是一个简单的例子,使用 subdirs
属性递归搜索两个目录以显示公共差异文件:
>>> from filecmp import dircmp
>>> def print_diff_files(dcmp):
... for name in dcmp.diff_files:
... print("diff_file %s found in %s and %s" % (name, dcmp.left,
... dcmp.right))
... for sub_dcmp in dcmp.subdirs.values():
... print_diff_files(sub_dcmp)
...
>>> dcmp = dircmp('dir1', 'dir2')
>>> print_diff_files(dcmp)
tempfile
—- 生成临时文件和目录
源代码: Lib/tempfile.py
该模块用于创建临时文件和目录,它可以跨平台使用。TemporaryFile
、NamedTemporaryFile
、TemporaryDirectory
和 SpooledTemporaryFile
是带有自动清理功能的高级接口,可用作上下文管理器。mkstemp()
和 mkdtemp()
是低级函数,使用完毕需手动清理。
所有由用户调用的函数和构造函数都带有参数,这些参数可以设置临时文件和临时目录的路径和名称。该模块生成的文件名包括一串随机字符,在公共的临时目录中,这些字符可以让创建文件更加安全。为了保持向后兼容性,参数的顺序有些奇怪。所以为了代码清晰,建议使用关键字参数。
这个模块定义了以下内容供用户调用:
tempfile.TemporaryFile
(mode=’w+b’, buffering=- 1, encoding=None, newline=None, suffix=None, prefix=None, dir=None, **, errors=None*)
返回一个 file-like object (文件类对象)作为临时存储区域。创建该文件使用了与 mkstemp()
相同的安全规则。它将在关闭后立即销毁(包括垃圾回收机制关闭该对象时)。在 Unix 下,该文件在目录中的条目根本不创建,或者创建文件后立即就被删除了,其他平台不支持此功能。您的代码不应依赖使用此功能创建的临时文件名称,因为它在文件系统中的名称可能是可见的,也可能是不可见的。
生成的对象可以用作上下文管理器。完成上下文或销毁临时文件对象后,临时文件将从文件系统中删除。
mode 参数默认值为 'w+b'
,所以创建的文件不用关闭,就可以读取或写入。因为用的是二进制模式,所以无论存的是什么数据,它在所有平台上都表现一致。buffering、encoding、errors 和 newline 的含义与 open()
中的相同。
参数 dir、prefix 和 suffix 的含义和默认值都与它们在 mkstemp()
中的相同。
在 POSIX 平台上,它返回的对象是真实的文件对象。在其他平台上,它是一个文件类对象 (file-like object),它的 file
属性是底层的真实文件对象。
如果可用,则使用 os.O_TMPFILE
标志(仅限于 Linux,需要 3.11 及更高版本的内核)。
引发一个 tempfile.mkstemp
审计事件,附带参数 fullpath
。
在 3.5 版更改: 如果可用,现在用的是 os.O_TMPFILE
标志。
在 3.8 版更改: 添加了 errors 参数。
tempfile.NamedTemporaryFile
(mode=’w+b’, buffering=- 1, encoding=None, newline=None, suffix=None, prefix=None, dir=None, delete=True, **, errors=None*)
此函数执行的操作与 TemporaryFile()
完全相同,但确保了该临时文件在文件系统中具有可见的名称(在 Unix 上表现为目录条目不取消链接)。从返回的文件类对象的 name
属性中可以检索到文件名。在临时文件仍打开时,是否允许用文件名第二次打开文件,在各个平台上是不同的(在 Unix 上可以,但在 Windows NT 或更高版本上不行)。如果 delete 为 true(默认值),则文件会在关闭后立即被删除。该函数返回的对象始终是文件类对象 (file-like object),它的 file
属性是底层的真实文件对象。文件类对象可以像普通文件一样在 with
语句中使用。
引发一个 tempfile.mkstemp
审计事件,附带参数 fullpath
。
在 3.8 版更改: 添加了 errors 参数。
tempfile.SpooledTemporaryFile
(max_size=0, mode=’w+b’, buffering=- 1, encoding=None, newline=None, suffix=None, prefix=None, dir=None, **, errors=None*)
此函数执行的操作与 TemporaryFile()
完全相同,但会将数据缓存在内存中,直到文件大小超过 max_size,或调用文件的 fileno()
方法为止,此时数据会被写入磁盘,并且写入操作与 TemporaryFile()
相同。
此函数生成的文件对象有一个额外的方法——rollover()
,可以忽略文件大小,让文件立即写入磁盘。
返回的对象是文件类对象 (file-like object),它的 _file
属性是 io.BytesIO
或 io.TextIOWrapper
对象(取决于指定的是二进制模式还是文本模式)或真实的文件对象(取决于是否已调用 rollover()
)。文件类对象可以像普通文件一样在 with
语句中使用。
在 3.3 版更改: 现在,文件的 truncate 方法可接受一个 size
参数。
在 3.8 版更改: 添加了 errors 参数。
tempfile.TemporaryDirectory
(suffix=None, prefix=None, dir=None, ignore_cleanup_errors=False)
此函数会使用与 mkdtemp()
相同的规则安全地创建一个临时目录。 结果对象可被用作上下文管理器。 在完成上下文或销毁临时目录对象时,新创建的临时目录及其所有内容会从文件系统中被移除。
可以从返回对象的 name
属性中找到临时目录的名称。当返回的对象用作上下文管理器时,这个 name
会作为 with
语句中 as
子句的目标(如果有 as 的话)。
此目录可通过调用 cleanup()
方法来显式地清理。 如果 ignore_cleanup_errors 为真值,则在显式或隐式清理(例如在 Windows 上 PermissionError
移除打开的文件)期间出现的未处理异常将被忽略,并且剩余的可移除条目会被“尽可能”地删除。 在其他情况下,错误将在任何上下文清理发生时被引发 (cleanup()
调用、退出上下文管理器、对象被作为垃圾回收或解释器关闭等)。
引发一个 tempfile.mkdtemp
审计事件,附带参数 fullpath
。
3.2 新版功能.
在 3.10 版更改: 添加了 ignore_cleanup_errors 形参。
tempfile.mkstemp
(suffix=None, prefix=None, dir=None, text=False)
以最安全的方式创建一个临时文件。假设所在平台正确实现了 os.open()
的 os.O_EXCL
标志,则创建文件时不会有竞争的情况。该文件只能由创建者读写,如果所在平台用权限位来标记文件是否可执行,那么没有人有执行权。文件描述符不会过继给子进程。
与 TemporaryFile()
不同,mkstemp()
用户用完临时文件后需要自行将其删除。
如果 suffix 不是 None
则文件名将以该后缀结尾,是 None
则没有后缀。mkstemp()
不会在文件名和后缀之间加点,如果需要加一个点号,请将其放在 suffix 的开头。
如果 prefix 不是 None
,则文件名将以该前缀开头,是 None
则使用默认前缀。默认前缀是 gettempprefix()
或 gettempprefixb()
函数的返回值(自动调用合适的函数)。
如果 dir 不为 None
,则在指定的目录创建文件,是 None
则使用默认目录。默认目录是从一个列表中选择出来的,这个列表不同平台不一样,但是用户可以设置 TMPDIR、TEMP 或 TMP 环境变量来设置目录的位置。因此,不能保证生成的临时文件路径很规范,比如,通过 os.popen()
将路径传递给外部命令时仍需要加引号。
如果 suffix、prefix 和 dir 中的任何一个不是 None
,就要保证它们是同一数据类型。如果它们是 bytes,则返回的名称的类型就是 bytes 而不是 str。如果确实要用默认参数,但又想要返回值是 bytes 类型,请传入 suffix=b''
。
如果指定了 text 且为真值,文件会以文本模式打开。 否则,文件(默认)会以二进制模式打开。
mkstemp()
返回一个元组,元组中第一个元素是句柄,它是一个系统级句柄,指向一个打开的文件(等同于 os.open()
的返回值),第二元素是该文件的绝对路径。
引发一个 tempfile.mkstemp
审计事件,附带参数 fullpath
。
在 3.5 版更改: 现在,suffix、prefix 和 dir 可以以 bytes 类型按顺序提供,以获得 bytes 类型的返回值。之前只允许使用 str。suffix 和 prefix 现在可以接受 None
,并且默认为 None
以使用合适的默认值。
在 3.6 版更改: dir 参数现在可接受一个路径类对象 (path-like object)。
tempfile.mkdtemp
(suffix=None, prefix=None, dir=None)
以最安全的方式创建一个临时目录,创建该目录时不会有竞争的情况。该目录只能由创建者读取、写入和搜索。
mkdtemp()
用户用完临时目录后需要自行将其删除。
prefix、suffix 和 dir 的含义与它们在 mkstemp()
中的相同。
mkdtemp()
返回新目录的绝对路径。
引发一个 tempfile.mkdtemp
审计事件,附带参数 fullpath
。
在 3.5 版更改: 现在,suffix、prefix 和 dir 可以以 bytes 类型按顺序提供,以获得 bytes 类型的返回值。之前只允许使用 str。suffix 和 prefix 现在可以接受 None
,并且默认为 None
以使用合适的默认值。
在 3.6 版更改: dir 参数现在可接受一个路径类对象 (path-like object)。
tempfile.gettempdir
()
返回放置临时文件的目录的名称。这个方法的返回值就是本模块所有函数的 dir 参数的默认值。
Python 搜索标准目录列表,以找到调用者可以在其中创建文件的目录。这个列表是:
TMPDIR
环境变量指向的目录。TEMP
环境变量指向的目录。TMP
环境变量指向的目录。- 与平台相关的位置:
- 在 Windows 上,依次为
C:\TEMP
、C:\TMP
、\TEMP
和\TMP
。 - 在所有其他平台上,依次为
/tmp
、/var/tmp
和/usr/tmp
。
- 在 Windows 上,依次为
- 不得已时,使用当前工作目录。
在 3.10 版更改: 总是返回一个字符串。 在之前的版本中它会返回任意 tempdir
值而不考虑它的类型,只要它不为 None
。
tempfile.gettempdirb
()
与 gettempdir()
相同,但返回值为字节类型。
3.5 新版功能.
tempfile.gettempprefix
()
返回用于创建临时文件的文件名前缀,它不包含目录部分。
tempfile.gettempprefixb
()
与 gettempprefix()
相同,但返回值为字节类型。
3.5 新版功能.
本模块使用一个全局变量来存储由 gettempdir()
返回的临时文件使用的目录路径。 它可被直接设置以覆盖选择过程,但不建议这样做。 本模块中的所有函数都接受一个 dir 参数,它可被用于指定目录。 这是不会通过改变全局 API 行为对其他无准备代码造成影响的推荐做法。
tempfile.tempdir
当设为 None
以外的值时,此变量会为本模块中定义的函数的 dir 参数定义默认值,包括确定其类型为字节串还是字符串。 它不可以为 path-like object。
如果在调用除 gettempprefix()
外的上述任何函数时 tempdir
为 None
(默认值) 则它会按照 gettempdir()
中所描述的算法来初始化。
注解
请注意如果你将 tempdir
设为字节串值,会有一个麻烦的副作用: mkstemp()
和 mkdtemp()
的全局默认返回类型会在没有显式提供字符串类型的when no explicit prefix
, suffix
或 dir
的时候被改为字节串。 请不要编写预期或依赖于此入围的代码。 这个笨拙行为是为了保持与历史实现的兼容性。
例子
以下是 tempfile
模块典型用法的一些示例:
>>> import tempfile
# create a temporary file and write some data to it
>>> fp = tempfile.TemporaryFile()
>>> fp.write(b'Hello world!')
# read data from file
>>> fp.seek(0)
>>> fp.read()
b'Hello world!'
# close the file, it will be removed
>>> fp.close()
# create a temporary file using a context manager
>>> with tempfile.TemporaryFile() as fp:
... fp.write(b'Hello world!')
... fp.seek(0)
... fp.read()
b'Hello world!'
>>>
# file is now closed and removed
# create a temporary directory using the context manager
>>> with tempfile.TemporaryDirectory() as tmpdirname:
... print('created temporary directory', tmpdirname)
>>>
# directory and contents have been removed
已弃用的函数和变量
创建临时文件有一种历史方法,首先使用 mktemp()
函数生成一个文件名,然后使用该文件名创建文件。不幸的是,这是不安全的,因为在调用 mktemp()
与随后尝试创建文件的进程之间的时间里,其他进程可能会使用该名称创建文件。解决方案是将两个步骤结合起来,立即创建文件。这个方案目前被 mkstemp()
和上述其他函数所采用。
tempfile.mktemp
(suffix=’’, prefix=’tmp’, dir=None)
2.3 版后已移除: 使用 mkstemp()
来代替。
返回一个绝对路径,这个路径指向的文件在调用本方法时不存在。prefix、suffix 和 dir 参数与 mkstemp()
中的同名参数类似,不同之处在于不支持字节类型的文件名,不支持 suffix=None
和 prefix=None
。
警告
使用此功能可能会在程序中引入安全漏洞。当你开始使用本方法返回的文件执行任何操作时,可能有人已经捷足先登了。mktemp()
的功能可以很轻松地用 NamedTemporaryFile()
代替,当然需要传递 delete=False
参数:
>>> f = NamedTemporaryFile(delete=False)
>>> f.name
'/tmp/tmptjujjt'
>>> f.write(b"Hello World!\n")
13
>>> f.close()
>>> os.unlink(f.name)
>>> os.path.exists(f.name)
False
glob
—- Unix 风格路径名模式扩展
源代码: Lib/glob.py
glob
模块可根据 Unix 终端所用规则找出所有匹配特定模式的路径名,但会按不确定的顺序返回结果。 波浪号扩展不会生效,但 *
, ?
以及表示为 []
的字符范围将被正确地匹配。 这是通过配合使用 os.scandir()
和 fnmatch.fnmatch()
函数来实现的,而不是通过实际发起调用子终端。 请注意不同于 fnmatch.fnmatch()
,glob
会将以点号 (.
) 开头的文件名作为特殊情况来处理。 (对于波浪号和终端变量扩展,请使用 os.path.expanduser()
和 os.path.expandvars()
。)
对于字面值匹配,请将原字符用方括号括起来。 例如,'[?]'
将匹配字符 '?'
。
参见
pathlib
模块提供高级路径对象。
glob.glob
(pathname, **, root_dir=None, dir_fd=None, recursive=False*)
返回匹配 pathname 的可能为空的路径名列表,其中的元素必须为包含路径信息的字符串。 pathname 可以是绝对路径 (如 /usr/src/Python-1.5/Makefile
) 或相对路径 (如 ../../Tools/*/*.gif
),并且可包含 shell 风格的通配符。 结果也将包含无效的符号链接(与在 shell 中一样)。 结果是否排序取决于具体文件系统。 如果某个符合条件的文件在调用此函数期间被移除或添加,是否包括该文件的路径是没有规定的。
如果 root_dir 不为 None
,则它应当是指明要搜索的根目录的 path-like object。 它用在 glob()
上与在调用它之前改变当前目录有相同的效果。 如果 pathname 为相对路径,结果将包含相对于 root_dir 的路径。
本函数带有 dir_fd 参数,支持 基于目录描述符的相对路径。
如果 recursive 为真值,则模式 “**
“ 将匹配目录中的任何文件以及零个或多个目录、子目录和符号链接。 如果模式加了一个 os.sep
或 os.altsep
则将不匹配文件。
引发一个 审计事件 glob.glob
附带参数 pathname
, recursive
。
引发一个 审计事件 glob.glob/2
,附带参数 pathname
, recursive
, root_dir
, dir_fd
。
注解
在一个较大的目录树中使用 “**
“ 模式可能会消耗非常多的时间。
在 3.5 版更改: 支持使用 “**
“ 的递归 glob。
在 3.10 版更改: 添加了 root_dir 和 dir_fd 形参。
glob.iglob
(pathname, **, root_dir=None, dir_fd=None, recursive=False*)
返回一个 iterator,它会产生与 glob()
相同的结果,但不会实际地同时保存它们。
引发一个 审计事件 glob.glob
附带参数 pathname
, recursive
。
引发一个 审计事件 glob.glob/2
,附带参数 pathname
, recursive
, root_dir
, dir_fd
。
在 3.5 版更改: 支持使用 “**
“ 的递归 glob。
在 3.10 版更改: 添加了 root_dir 和 dir_fd 形参。
glob.escape
(pathname)
转义所有特殊字符 ('?'
, '*'
和 '['
)。 这适用于当你想要匹配可能带有特殊字符的任意字符串字面值的情况。 在 drive/UNC 共享点中的特殊字符不会被转义,例如在 Windows 上 escape('//?/c:/Quo vadis?.txt')
将返回 '//?/c:/Quo vadis[?].txt'
。
3.4 新版功能.
例如,考虑一个包含以下内容的目录:文件 1.gif
, 2.txt
, card.gif
以及一个子目录 sub
其中只包含一个文件 3.txt
. glob()
将产生如下结果。 请注意路径的任何开头部分都将被保留。:
>>> import glob
>>> glob.glob('./[0-9].*')
['./1.gif', './2.txt']
>>> glob.glob('*.gif')
['1.gif', 'card.gif']
>>> glob.glob('?.gif')
['1.gif']
>>> glob.glob('**/*.txt', recursive=True)
['2.txt', 'sub/3.txt']
>>> glob.glob('./**/', recursive=True)
['./', './sub/']
如果目录包含以 .
打头的文件,它们默认将不会被匹配。 例如,考虑一个包含 card.gif
和 .card.gif
的目录:
>>> import glob
>>> glob.glob('*.gif')
['card.gif']
>>> glob.glob('.c*')
['.card.gif']
fnmatch
—- Unix 文件名模式匹配
源代码: Lib/fnmatch.py
此模块提供了 Unix shell 风格的通配符,它们 并不 等同于正则表达式。 shell 风格通配符所使用的特殊字符如下:
模式 | 含意 |
---|---|
* |
匹配所有 |
? |
匹配任何单个字符 |
[seq] |
匹配 seq 中的任何字符 |
[!seq] |
匹配任何不在 seq 中的字符 |
对于字面值匹配,请将原字符用方括号括起来。 例如,'[?]'
将匹配字符 '?'
。
注意文件名分隔符 (Unix 上为 '/'
) 不是 此模块所特有的。 请参见 glob
模块了解文件名扩展 (glob
使用 filter()
来匹配文件名的各个部分)。 类似地,以一个句点打头的文件名也不是此模块所特有的,可以通过 *
和 ?
模式来匹配。
fnmatch.fnmatch
(filename, pattern)
检测 filename 字符串是否匹配 pattern 字符串,返回 True
或 False
。 两个形参都会使用 os.path.normcase()
进行大小写正规化。 fnmatchcase()
可被用于执行大小写敏感的比较,无论这是否为所在操作系统的标准。
这个例子将打印当前目录下带有扩展名 .txt
的所有文件名:
import fnmatch
import os
for file in os.listdir('.'):
if fnmatch.fnmatch(file, '*.txt'):
print(file)
fnmatch.fnmatchcase
(filename, pattern)
检测 filename 是否匹配 pattern,返回 True
或 False
;此比较是大小写敏感的,并且不会应用 os.path.normcase()
。
fnmatch.filter
(names, pattern)
基于可迭代对象 names 中匹配 pattern 的元素构造一个列表。 它等价于 [n for n in names if fnmatch(n, pattern)]
,但实现得更有效率。
fnmatch.translate
(pattern)
返回 shell 风格 pattern 转换成的正则表达式以便用于 re.match()
。
示例:
>>> import fnmatch, re
>>>
>>> regex = fnmatch.translate('*.txt')
>>> regex
'(?s:.*\\.txt)\\Z'
>>> reobj = re.compile(regex)
>>> reobj.match('foobar.txt')
<re.Match object; span=(0, 10), match='foobar.txt'>
linecache
—- 随机读写文本行
源代码: Lib/linecache.py
linecache
模块允许从一个 Python 源文件中获取任意的行,并会尝试使用缓存进行内部优化,常应用于从单个文件读取多行的场合。 此模块被 traceback
模块用来提取源码行以便包含在格式化的回溯中。
tokenize.open()
函数被用于打开文件。 此函数使用 tokenize.detect_encoding()
来获取文件的编码格式;如果未指明编码格式,则默认编码为 UTF-8。
linecache
模块定义了下列函数:
linecache.getline
(filename, lineno, module_globals=None)
从名为 filename 的文件中获取 lineno 行,此函数绝不会引发异常 —- 出现错误时它将返回 ''
(所有找到的行都将包含换行符作为结束)。
如果找不到名为 filename 的文件,此函数会先在 module_globals 中检查 PEP 302 __loader__
。 如果存在这样的加载器并且它定义了 get_source
方法,则由该方法来确定源行 (如果 get_source()
返回 None
,则该函数返回 ''
)。 最后,如果 filename 是一个相对路径文件名,则它会在模块搜索路径 sys.path
中按条目的相对位置进行查找。
linecache.clearcache
()
清空缓存。 如果你不再需要之前使用 getline()
从文件读取的行即可使用此函数。
linecache.checkcache
(filename=None)
检查缓存有效性。 如果缓存中的文件在磁盘上发生了改变,而你需要更新后的版本即可使用此函数。 如果省略了 filename,它会检查缓存中的所有条目。
linecache.lazycache
(filename, module_globals)
捕获有关某个非基于文件的模块的足够细节信息,以允许稍后再通过 getline()
来获取其中的行,即使当稍后调用时 module_globals 为 None
。 这可以避免在实际需要读取行之前执行 I/O,也不必始终保持模块全局变量。
3.5 新版功能.
示例:
>>> import linecache
>>> linecache.getline(linecache.__file__, 8)
'import sys\n'
shutil
—- 高阶文件操作
源代码: Lib/shutil.py
shutil
模块提供了一系列对文件和文件集合的高阶操作。 特别是提供了一些支持文件拷贝和删除的函数。
警告
即便是高阶文件拷贝函数 (shutil.copy()
, shutil.copy2()
) 也无法拷贝所有的文件元数据。
在 POSIX 平台上,这意味着将丢失文件所有者和组以及 ACL 数据。 在 Mac OS 上,资源钩子和其他元数据不被使用。 这意味着将丢失这些资源并且文件类型和创建者代码将不正确。 在 Windows 上,将不会拷贝文件所有者、ACL 和替代数据流。
目录和文件操作
shutil.copyfileobj
(fsrc, fdst[, length])
将文件类对象 fsrc 的内容拷贝到文件类对象 fdst*。 整数值 *length 如果给出则为缓冲区大小。 特别地, length 为负值表示拷贝数据时不对源数据进行分块循环处理;默认情况下会分块读取数据以避免不受控制的内存消耗。 请注意如果 fsrc 对象的当前文件位置不为 0,则只有从当前文件位置到文件末尾的内容会被拷贝。
shutil.copyfile
(src, dst, **, follow_symlinks=True*)
将名为 src 的文件的内容(不包括元数据)拷贝到名为 dst 的文件并以尽可能高效的方式返回 dst*。 *src 和 dst 均为路径类对象或以字符串形式给出的路径名。
dst 必须是完整的目标文件名。 如果 src 和 dst 指定了同一个文件,则将引发 SameFileError
。
目标位置必须是可写的;否则将引发 OSError
异常。 如果 dst 已经存在,它将被替换。 特殊文件如字符或块设备以及管道无法用此函数来拷贝。
如果 follow_symlinks 为假值且 src 为符号链接,则将创建一个新的符号链接而不是拷贝 src 所指向的文件。
引发一个 审计事件 shutil.copyfile
附带参数 src
, dst
。
在 3.3 版更改: 曾经是引发 IOError
而不是 OSError
。 增加了 follow_symlinks 参数。 现在是返回 dst。
在 3.4 版更改: 引发 SameFileError
而不是 Error
。 由于前者是后者的子类,此改变是向后兼容的。
在 3.8 版更改: 可能会在内部使用平台专属的快速拷贝系统调用以更高效地拷贝文件。
exception shutil.SameFileError
此异常会在 copyfile()
中的源和目标为同一文件时被引发。
3.4 新版功能.
shutil.copymode
(src, dst, **, follow_symlinks=True*)
从 src 拷贝权限位到 dst*。 文件的内容、所有者和分组将不受影响。 *src 和 dst 均为路径类对象或字符串形式的路径名。 如果 follow_symlinks 为假值,并且 src 和 dst 均为符号链接,copymode()
将尝试修改 dst 本身的模式(而非它所指向的文件)。 此功能并不是在所有平台上均可用;请参阅 copystat()
了解详情。 如果 copymode()
无法修改本机平台上的符号链接,而它被要求这样做,它将不做任何操作即返回。
引发一个 审计事件 shutil.copymode
附带参数 src
, dst
。
在 3.3 版更改: 加入 follow_symlinks 参数。
shutil.copystat
(src, dst, **, follow_symlinks=True*)
从 src 拷贝权限位、最近访问时间、最近修改时间以及旗标到 dst*。 在 Linux上,copystat()
还会在可能的情况下拷贝“扩展属性”。 文件的内容、所有者和分组将不受影响。 *src 和 dst 均为路径类对象或字符串形式的路径名。
如果 follow_symlinks 为假值,并且 src 和 dst 均指向符号链接,copystat()
将作用于符号链接本身而非该符号链接所指向的文件 — 从 src 符号链接读取信息,并将信息写入 dst 符号链接。
注解
并非所有平台者提供检查和修改符号链接的功能。 Python 本身可以告诉你哪些功能是在本机上可用的。
- 如果
os.chmod in os.supports_follow_symlinks
为True
,则copystat()
可以修改符号链接的权限位。 - 如果
os.utime in os.supports_follow_symlinks
为True
,则copystat()
可以修改符号链接的最近访问和修改时间。 - 如果
os.chflags in os.supports_follow_symlinks
为True
,则copystat()
可以修改符号链接的旗标。 (os.chflags
不是在所有平台上均可用。)
在此功能部分或全部不可用的平台上,当被要求修改一个符号链接时,copystat()
将尽量拷贝所有内容。 copystat()
一定不会返回失败信息。
引发一个 审计事件 shutil.copystat
附带参数 src
, dst
。
在 3.3 版更改: 添加了 follow_symlinks 参数并且支持 Linux 扩展属性。
shutil.copy
(src, dst, **, follow_symlinks=True*)
将文件 src 拷贝到文件或目录 dst*。 *src 和 dst 应为 路径类对象 或字符串。 如果 dst 指定了一个目录,文件将使用 src 中的基准文件名拷贝到 dst 中。 将返回新创建文件所对应的路径。
如果 follow_symlinks 为假值且 src 为符号链接,则 dst 也将被创建为符号链接。 如果 follow_symlinks 为真值且 src 为符号链接,dst 将成为 src 所指向的文件的一个副本。
copy()
会拷贝文件数据和文件的权限模式 (参见 os.chmod()
)。 其他元数据,例如文件的创建和修改时间不会被保留。 要保留所有原有的元数据,请改用 copy2()
。
引发一个 审计事件 shutil.copyfile
附带参数 src
, dst
。
引发一个 审计事件 shutil.copymode
附带参数 src
, dst
。
在 3.3 版更改: 添加了 follow_symlinks 参数。 现在会返回新创建文件的路径。
在 3.8 版更改: 可能会在内部使用平台专属的快速拷贝系统调用以更高效地拷贝文件。
shutil.copy2
(src, dst, **, follow_symlinks=True*)
类似于 copy()
,区别在于 copy2()
还会尝试保留文件的元数据。
当 follow_symlinks 为假值且 src 为符号链接时,copy2()
会尝试将来自 src 符号链接的所有元数据拷贝到新创建的 dst 符号链接。 但是,此功能不是在所有平台上均可用。 在此功能部分或全部不可用的平台上,copy2()
将尽量保留所有元数据;copy2()
一定不会由于无法保留文件元数据而引发异常。
copy2()
会使用 copystat()
来拷贝文件元数据。
引发一个 审计事件 shutil.copyfile
附带参数 src
, dst
。
引发一个 审计事件 shutil.copystat
附带参数 src
, dst
。
在 3.3 版更改: 添加了 follow_symlinks 参数,还会尝试拷贝扩展文件系统属性(目前仅限 Linux)。 现在会返回新创建文件的路径。
在 3.8 版更改: 可能会在内部使用平台专属的快速拷贝系统调用以更高效地拷贝文件。
shutil.ignore_patterns
(\patterns*)
这个工厂函数会创建一个函数,它可被用作 copytree()
的 ignore 可调用对象参数,以忽略那些匹配所提供的 glob 风格的 patterns 之一的文件和目录。 参见以下示例。
shutil.copytree
(src, dst, symlinks=False, ignore=None, copy_function=copy2, ignore_dangling_symlinks=False, dirs_exist_ok=False)
将以 src 为根起点的整个目录树拷贝到名为 dst 的目录并返回目标目录。 dirs_exist_ok 指明是否要在 dst 或任何丢失的父目录已存在的情况下引发异常。
目录的权限和时间会通过 copystat()
来拷贝,单个文件则会使用 copy2()
来拷贝。
如果 symlinks 为真值,源目录树中的符号链接会在新目录树中表示为符号链接,并且原链接的元数据在平台允许的情况下也会被拷贝;如果为假值或省略,则会将被链接文件的内容和元数据拷贝到新目录树。
当 symlinks 为假值时,如果符号链接所指向的文件不存在,则会在拷贝进程的末尾将一个异常添加到 Error
异常中的错误列表。 如果你希望屏蔽此异常那就将可选的 ignore_dangling_symlinks 旗标设为真值。 请注意此选项在不支持 os.symlink()
的平台上将不起作用。
如果给出了 ignore,它必须是一个可调用对象,该对象将接受 copytree()
所访问的目录以及 os.listdir()
所返回的目录内容列表作为其参数。 由于 copytree()
是递归地被调用的,ignore 可调用对象对于每个被拷贝目录都将被调用一次。 该可调用对象必须返回一个相对于当前目录的目录和文件名序列(即其第二个参数的子集);随后这些名称将在拷贝进程中被忽略。 ignore_patterns()
可被用于创建这种基于 glob 风格模式来忽略特定名称的可调用对象。
如果发生了(一个或多个)异常,将引发一个附带原因列表的 Error
。
如果给出了 copy_function,它必须是一个将被用来拷贝每个文件的可调用对象。 它在被调用时会将源路径和目标路径作为参数传入。 默认情况下,copy2()
将被使用,但任何支持同样签名(与 copy()
一致)都可以使用。
引发一个 审计事件 shutil.copytree
附带参数 src
, dst
。
在 3.3 版更改: 当 symlinks 为假值时拷贝元数据。 现在会返回 dst。
在 3.2 版更改: 添加了 copy_function 参数以允许提供定制的拷贝函数。 添加了 ignore_dangling_symlinks 参数以便在 symlinks 为假值时屏蔽符号链接错误。
在 3.8 版更改: 可能会在内部使用平台专属的快速拷贝系统调用以更高效地拷贝文件。
3.8 新版功能: dirs_exist_ok 形参。
shutil.rmtree
(path, ignore_errors=False, onerror=None)
删除一个完整的目录树;path 必须指向一个目录(但不能是一个目录的符号链接)。 如果 ignore_errors 为真值,删除失败导致的错误将被忽略;如果为假值或是省略,此类错误将通过调用由 onerror 所指定的处理程序来处理,或者如果此参数被省略则将引发一个异常。
注解
在支持必要的基于 fd 的函数的平台上,默认会使用 rmtree()
的可防御符号链接攻击的版本。 在其他平台上,rmtree()
较易遭受符号链接攻击:给定适当的时间和环境,攻击者可以操纵文件系统中的符号链接来删除他们在其他情况下无法访问的文件。 应用程序可以使用 rmtree.avoids_symlink_attacks
函数属性来确定此类情况具体是哪一些。
如果提供了 onerror*,它必须为接受三个形参的可调用对象: *function, path 和 excinfo。
第一个形参 function 是引发异常的函数;它依赖于具体的平台和实现。 第二个形参 path 将是传递给 function 的路径名。 第三个形参 excinfo 将是由 sys.exc_info()
所返回的异常信息。 由 onerror 所引发的异常将不会被捕获。
引发一个 审计事件 shutil.rmtree
附带参数 path
。
在 3.3 版更改: 添加了一个防御符号链接攻击的版本,如果平台支持基于 fd 的函数就会被使用。
在 3.8 版更改: 在 Windows 上将不会再在移除连接之前删除目录连接中的内容。
rmtree.avoids_symlink_attacks
指明当前平台和实现是否提供防御符号链接攻击的
rmtree()
版本。 目前它仅在平台支持基于 fd 的目录访问函数时才返回真值。3.3 新版功能.
shutil.move
(src, dst, copy_function=copy2)
递归地将一个文件或目录 (src) 移至另一位置 (dst) 并返回目标位置。
如果目标是已存在的目录,则 src 会被移至该目录下。 如果目标已存在但不是目录,它可能会被覆盖,具体取决于 os.rename()
的语义。
如果目标是在当前文件系统中,则会使用 os.rename()
。 在其他情况下,src 将被拷贝至 dst,使用的函数为 *copy_function,然后目标会被移除。 对于符号链接,则将在 *dst 之下或以其本身为名称创建一个指向 src 目标的新符号链接,并且 src 将被移除。
如果给出了 copy_function*,则它必须为接受两个参数 *src 和 dst 的可调用对象,并将在 os.rename()
无法使用时被用来将 src 拷贝到 dst*。 如果源是一个目录,则会调用 copytree()
,并向它传入 copy_function()
。 默认的 *copy_function 是 copy2()
。 使用 copy()
作为 copy_function 允许在无法附带拷贝元数据时让移动操作成功执行,但其代价是不拷贝任何元数据。
引发一个 审计事件 shutil.move
附带参数 src
, dst
。
在 3.3 版更改: 为异类文件系统添加了显式的符号链接处理,以便使它适应 GNU 的 mv 的行为。 现在会返回 dst。
在 3.5 版更改: 增加了 copy_function 关键字参数。
在 3.8 版更改: 可能会在内部使用平台专属的快速拷贝系统调用以更高效地拷贝文件。
在 3.9 版更改: 接受一个 path-like object 作为 src 和 dst。
shutil.disk_usage
(path)
返回给定路径的磁盘使用统计数据,形式为一个 named tuple,其中包含 total, used 和 free 属性,分别表示总计、已使用和未使用空间的字节数。 path 可以是一个文件或是一个目录。
3.3 新版功能.
在 3.8 版更改: 在 Windows 上,path 现在可以是一个文件或目录。
可用性: Unix, Windows。
shutil.chown
(path, user=None, group=None)
修改给定 path 的所有者 user 和/或 group。
user 可以是一个系统用户名或 uid;group 同样如此。 要求至少有一个参数。
另请参阅下层的函数 os.chown()
。
引发一个 审计事件 shutil.chown
附带参数 path
, user
, group
。
可用性: Unix。
3.3 新版功能.
shutil.which
(cmd, mode=os.F_OK | os.X_OK, path=None)
返回当给定的 cmd 被调用时将要运行的可执行文件的路径。 如果没有 cmd 会被调用则返回 None
。
mode 是一个传递给 os.access()
的权限掩码,在默认情况下将确定文件是否存在并且为可执行文件。
当未指定 path 时,将会使用 os.environ()
的结果,返回 “PATH” 的值或回退为 os.defpath
。
在 Windows 上当前目录总是会被添加为 path 的第一项,无论你是否使用默认值或提供你自己的路径,这是命令行终端在查找可执行文件时所采用的行为方式。 此外,当在 path 中查找 cmd 时,还会检查 PATHEXT
环境变量。 例如,如果你调用 shutil.which("python")
,which()
将搜索 PATHEXT
来确定它要在 path 目录中查找 python.exe
。 例如,在 Windows 上:
>>> shutil.which("python")
'C:\\Python33\\python.EXE'
3.3 新版功能.
在 3.8 版更改: 现在可以接受 bytes
类型。 如果 cmd 的类型为 bytes
,结果的类型也将为 bytes
。
exception shutil.Error
此异常会收集在多文件操作期间所引发的异常。 对于 copytree()
,此异常参数将是一个由三元组 (srcname, dstname, exception) 构成的列表。
依赖于具体平台的高效拷贝操作
从 Python 3.8 开始,所有涉及文件拷贝的函数 (copyfile()
, copy()
, copy2()
, copytree()
以及 move()
) 将会使用平台专属的 “fast-copy” 系统调用以便更高效地拷贝文件 。 “fast-copy” 意味着拷贝操作将发生于内核之中,避免像在 “outfd.write(infd.read())
“ 中那样使用 Python 用户空间的缓冲区。
在 macOS 上将会使用 fcopyfile 来拷贝文件内容(不含元数据)。
在 Linux 上将会使用 os.sendfile()
。
在 Windows 上 shutil.copyfile()
将会使用更大的默认缓冲区(1 MiB 而非 64 KiB)并且会使用基于 memoryview()
的 shutil.copyfileobj()
变种形式。
如果快速拷贝操作失败并且没有数据被写入目标文件,则 shutil 将在内部静默地回退到使用效率较低的 copyfileobj()
函数。
在 3.8 版更改.
copytree 示例
这个示例就是上面所描述的 copytree()
函数的实现,其中省略了文档字符串。 它还展示了此模块所提供的许多其他函数。
def copytree(src, dst, symlinks=False):
names = os.listdir(src)
os.makedirs(dst)
errors = []
for name in names:
srcname = os.path.join(src, name)
dstname = os.path.join(dst, name)
try:
if symlinks and os.path.islink(srcname):
linkto = os.readlink(srcname)
os.symlink(linkto, dstname)
elif os.path.isdir(srcname):
copytree(srcname, dstname, symlinks)
else:
copy2(srcname, dstname)
# XXX What about devices, sockets etc.?
except OSError as why:
errors.append((srcname, dstname, str(why)))
# catch the Error from the recursive copytree so that we can
# continue with other files
except Error as err:
errors.extend(err.args[0])
try:
copystat(src, dst)
except OSError as why:
# can't copy file access times on Windows
if why.winerror is None:
errors.extend((src, dst, str(why)))
if errors:
raise Error(errors)
另一个使用 ignore_patterns()
辅助函数的例子:
from shutil import copytree, ignore_patterns
copytree(source, destination, ignore=ignore_patterns('*.pyc', 'tmp*'))
这将会拷贝除 .pyc
文件和以 tmp
打头的文件或目录以外的所有条目.
另一个使用 ignore 参数来添加记录调用的例子:
from shutil import copytree
import logging
def _logpath(path, names):
logging.info('Working in %s', path)
return [] # nothing will be ignored
copytree(source, destination, ignore=_logpath)
rmtree 示例
这个例子演示了如何在 Windows 上删除一个目录树,其中部分文件设置了只读属性位。 它会使用 onerror 回调函数来清除只读属性位并再次尝试删除。 任何后续的失败都将被传播。
import os, stat
import shutil
def remove_readonly(func, path, _):
"Clear the readonly bit and reattempt the removal"
os.chmod(path, stat.S_IWRITE)
func(path)
shutil.rmtree(directory, onerror=remove_readonly)
归档操作
3.2 新版功能.
在 3.5 版更改: 添加了对 xztar 格式的支持。
本模块也提供了用于创建和读取压缩和归档文件的高层级工具。 它们依赖于 zipfile
和 tarfile
模块。
shutil.make_archive
(base_name, format[, root_dir[, base_dir[, verbose[, dry_run[, owner[, group[, logger]]]]]]])
创建一个归档文件(例如 zip 或 tar)并返回其名称。
base_name 是要创建的文件名称,包括路径,去除任何特定格式的扩展名。 format 是归档格式:为 “zip” (如果 zlib
模块可用), “tar”, “gztar” (如果 zlib
模块可用), “bztar” (如果 bz2
模块可用) 或 “xztar” (如果 lzma
模块可用) 中的一个。
root_dir 是一个目录,它将作为归档文件的根目录,归档中的所有路径都将是它的相对路径;例如,我们通常会在创建归档之前用 chdir 命令切换到 root_dir。
base_dir 是我们要执行归档的起始目录;也就是说 base_dir 将成为归档中所有文件和目录共有的路径前缀。 base_dir 必须相对于 root_dir 给出。
root_dir 和 base_dir 默认均为当前目录。
如果 dry_run 为真值,则不会创建归档文件,但将要被执行的操作会被记录到 logger。
owner 和 group 将在创建 tar 归档文件时被使用。 默认会使用当前的所有者和分组。
logger 必须是一个兼容 PEP 282 的对象,通常为 logging.Logger
的实例。
verbose 参数已不再使用并进入弃用状态。
引发一个 审计事件 shutil.make_archive
并附带参数 base_name
, format
, root_dir
, base_dir
。
注解
这个函数不是线程安全的。
在 3.8 版更改: 现在对于通过 format="tar"
创建的归档文件将使用新式的 pax (POSIX.1-2001) 格式而非旧式的 GNU 格式。
shutil.get_archive_formats
()
返回支持的归档格式列表。 所返回序列中的每个元素为一个元组 (name, description)
。
默认情况下 shutil
提供以下格式:
- zip: ZIP 文件(如果
zlib
模块可用)。 - tar: 未压缩的 tar 文件。 对于新归档文件将使用 POSIX.1-2001 pax 格式。
- gztar: gzip 压缩的 tar 文件(如果
zlib
模块可用)。 - bztar: bzip2 压缩的 tar 文件(如果
bz2
模块可用)。 - xztar: xz 压缩的 tar 文件(如果
lzma
模块可用)。
你可以通过使用 register_archive_format()
注册新的格式或为任何现有格式提供你自己的归档器。
shutil.register_archive_format
(name, function[, extra_args[, description]])
为 name 格式注册一个归档器。
function 是将被用来解包归档文件的可调用对象。 该可调用对象将接收要创建文件的 base_name*,再加上要归档内容的 *base_dir (其默认值为 os.curdir
)。 更多参数会被作为关键字参数传入: owner, group, dry_run 和 logger (与向 make_archive()
传入的参数一致)。
如果给出了 extra_args,则其应为一个 (name, value)
对的序列,将在归档器可调用对象被使用时作为附加的关键字参数。
description 由 get_archive_formats()
使用,它将返回归档器的列表。 默认值为一个空字符串。
shutil.unregister_archive_format
(name)
从支持的格式中移除归档格式 name。
shutil.unpack_archive
(filename[, extract_dir[, format]])
解包一个归档文件。 filename 是归档文件的完整路径。
extract_dir 是归档文件解包的目标目录名称。 如果未提供,则将使用当前工作目录。
format 是归档格式:应为 “zip”, “tar”, “gztar”, “bztar” 或 “xztar” 之一。 或者任何通过 register_unpack_format()
注册的其他格式。 如果未提供,unpack_archive()
将使用归档文件的扩展名来检查是否注册了对应于该扩展名的解包器。 在未找到任何解包器的情况下,将引发 ValueError
。
引发一个 审计事件 shutil.unpack_archive
附带参数 filename
, extract_dir
, format
。
在 3.7 版更改: 接受一个 path-like object 作为 filename 和 extract_dir。
shutil.register_unpack_format
(name, extensions, function[, extra_args[, description]])
注册一个解包格式。 name 为格式名称而 extensions 为对应于该格式的扩展名列表,例如 Zip 文件的扩展名为 .zip
。
function 是将被用来解包归档文件的可调用对象。 该可调用对象将接受归档文件的路径,加上该归档文件要被解包的目标目录。
如果提供了 extra_args,则其应为一个 (name, value)
元组的序列,将被作为关键字参数传递给该可调用对象。
可以提供 description 来描述该格式,它将被 get_unpack_formats()
返回。
shutil.unregister_unpack_format
(name)
撤销注册一个解包格式。 name 为格式的名称。
shutil.get_unpack_formats
()
返回所有已注册的解包格式列表。 所返回序列中的每个元素为一个元组 (name, extensions, description)
。
默认情况下 shutil
提供以下格式:
- zip: ZIP 文件(只有在相应模块可用时才能解包压缩文件)。
- tar: 未压缩的 tar 文件。
- gztar: gzip 压缩的 tar 文件(如果
zlib
模块可用)。 - bztar: bzip2 压缩的 tar 文件(如果
bz2
模块可用)。 - xztar: xz 压缩的 tar 文件(如果
lzma
模块可用)。
你可以通过使用 register_unpack_format()
注册新的格式或为任何现有格式提供你自己的解包器。
归档程序示例
在这个示例中,我们创建了一个 gzip 压缩的 tar 归档文件,其中包含用户的 .ssh
目录下的所有文件:
>>> from shutil import make_archive
>>> import os
>>> archive_name = os.path.expanduser(os.path.join('~', 'myarchive'))
>>> root_dir = os.path.expanduser(os.path.join('~', '.ssh'))
>>> make_archive(archive_name, 'gztar', root_dir)
'/Users/tarek/myarchive.tar.gz'
结果归档文件中包含有:
$ tar -tzvf /Users/tarek/myarchive.tar.gz
drwx------ tarek/staff 0 2010-02-01 16:23:40 ./
-rw-r--r-- tarek/staff 609 2008-06-09 13:26:54 ./authorized_keys
-rwxr-xr-x tarek/staff 65 2008-06-09 13:26:54 ./config
-rwx------ tarek/staff 668 2008-06-09 13:26:54 ./id_dsa
-rwxr-xr-x tarek/staff 609 2008-06-09 13:26:54 ./id_dsa.pub
-rw------- tarek/staff 1675 2008-06-09 13:26:54 ./id_rsa
-rw-r--r-- tarek/staff 397 2008-06-09 13:26:54 ./id_rsa.pub
-rw-r--r-- tarek/staff 37192 2010-02-06 18:23:10 ./known_hosts
使用 base_dir 的归档程序示例
在这个例子中,与 上面的例子 类似,我们演示了如何使用 make_archive()
,但这次是使用 base_dir。 我们现在具有如下的目录结构:
$ tree tmp
tmp
└── root
└── structure
├── content
└── please_add.txt
└── do_not_add.txt
在最终的归档中,应当会包括 please_add.txt
,但不应当包括 do_not_add.txt
。 因此我们使用以下代码:
>>> from shutil import make_archive
>>> import os
>>> archive_name = os.path.expanduser(os.path.join('~', 'myarchive'))
>>> make_archive(
... archive_name,
... 'tar',
... root_dir='tmp/root',
... base_dir='structure/content',
... )
'/Users/tarek/my_archive.tar'
列出结果归档中的文件我们将会得到:
$ python -m tarfile -l /Users/tarek/myarchive.tarstructure/content/structure/content/please_add.txt
查询输出终端的尺寸
shutil.get_terminal_size
(fallback=columns, lines)
获取终端窗口的尺寸。
对于两个维度中的每一个,会分别检查环境变量 COLUMNS
和 LINES
。 如果定义了这些变量并且其值为正整数,则将使用这些值。
如果未定义 COLUMNS
或 LINES
,这是通常的情况,则连接到 sys.__stdout__
的终端将通过发起调用 os.get_terminal_size()
被查询。
如果由于系统不支持查询,或是由于我们未连接到某个终端而导致查询终端尺寸不成功,则会使用在 fallback
形参中给出的值。 fallback
默认为 (80, 24)
,这是许多终端模拟器所使用的默认尺寸。
返回的值是一个 os.terminal_size
类型的具名元组。
3.3 新版功能.
数据持久化
pickle
和 marshal
模块可以将许多 Python 数据类型转换为字节流,然后从字节中重新创建对象。 各种与 DBM 相关的模块支持一系列基于散列的文件格式,这些格式存储字符串到其他字符串的映射。
本章中描述的模块列表是:
pickle
—- Python 对象序列化- 与其他 Python 模块间的关系
- 与
marshal
间的关系 - 与
json
模块的比较
- 与
- 数据流格式
- 模块接口
- 可以被封存/解封的对象
- 封存类实例
- 持久化外部对象
- Dispatch 表
- 处理有状态的对象
- 类型,函数和其他对象的自定义归约
- 外部缓冲区
- 提供方 API
- 使用方 API
- 示例
- 限制全局变量
- 性能
- 例子
- 与其他 Python 模块间的关系
copyreg
—- 注册配合pickle
模块使用的函数- 示例
shelve
—- Python 对象持久化- 限制
- 示例
marshal
—- 内部 Python 对象序列化dbm
—- Unix “数据库” 接口dbm.gnu
—- GNU 对 dbm 的重解析dbm.ndbm
—- 基于 ndbm 的接口dbm.dumb
—- 便携式 DBM 实现
sqlite3
—- SQLite 数据库 DB-API 2.0 接口模块- 模块函数和常量
- 连接对象(Connection)
- Cursor 对象
- 行对象
- 异常
- SQLite 与 Python 类型
- 概述
- 使用适配器将额外的 Python 类型保存在 SQLite 数据库中。
- 让对象自行适配
- 注册可调用的适配器
- 将SQLite 值转换为自定义Python 类型
- 默认适配器和转换器
- 控制事务
- 有效使用
sqlite3
- 使用快捷方式
- 通过名称而不是索引访问索引
- 使用连接作为上下文管理器
pickle
—- Python 对象序列化
源代码:Lib/pickle.py
模块 pickle
实现了对一个 Python 对象结构的二进制序列化和反序列化。 “pickling” 是将 Python 对象及其所拥有的层次结构转化为一个字节流的过程,而 “unpickling” 是相反的操作,会将(来自一个 binary file 或者 bytes-like object 的)字节流转化回一个对象层次结构。 pickling(和 unpickling)也被称为“序列化”, “编组” 或者 “平面化”。而为了避免混乱,此处采用术语 “封存 (pickling)” 和 “解封 (unpickling)”。
警告
pickle
模块并不安全。你只应该对你信任的数据进行unpickle操作。
构建恶意的 pickle 数据来在解封时执行任意代码是可能的。绝对不要对不信任来源的数据和可能被篡改过的数据进行解封。
请考虑使用 hmac
来对数据进行签名,确保数据没有被篡改。
在你处理不信任数据时,更安全的序列化格式如 json
可能更为适合.
与其他 Python 模块间的关系
与 marshal
间的关系
Python 有一个更原始的序列化模块称为 marshal
,但一般地 pickle
应该是序列化 Python 对象时的首选。marshal
存在主要是为了支持 Python 的 .pyc
文件.
pickle
模块与 marshal
在如下几方面显著地不同:
pickle
模块会跟踪已被序列化的对象,所以该对象之后再次被引用时不会再次被序列化。marshal
不会这么做。这隐含了递归对象和共享对象。递归对象指包含对自己的引用的对象。这种对象并不会被 marshal 接受,并且实际上尝试 marshal 递归对象会让你的 Python 解释器崩溃。对象共享发生在对象层级中存在多处引用同一对象时。
pickle
只会存储这些对象一次,并确保其他的引用指向同一个主副本。共享对象将保持共享,这可能对可变对象非常重要。marshal
不能被用于序列化用户定义类及其实例。pickle
能够透明地存储并保存类实例,然而此时类定义必须能够从与被存储时相同的模块被引入。同样用于序列化的
marshal
格式不保证数据能移植到不同的 Python 版本中。因为它的主要任务是支持.pyc
文件,必要时会以破坏向后兼容的方式更改这种序列化格式,为此 Python 的实现者保留了更改格式的权利。pickle
序列化格式可以在不同版本的 Python 中实现向后兼容,前提是选择了合适的 pickle 协议。如果你的数据要在 Python 2 与 Python 3 之间跨越传递,封存和解封的代码在 2 和 3 之间也是不同的。
与 json
模块的比较
Pickle 协议和 JSON (JavaScript Object Notation) 间有着本质的不同:
- JSON 是一个文本序列化格式(它输出 unicode 文本,尽管在大多数时候它会接着以
utf-8
编码),而 pickle 是一个二进制序列化格式; - JSON 是我们可以直观阅读的,而 pickle 不是;
- JSON是可互操作的,在Python系统之外广泛使用,而pickle则是Python专用的;
- 默认情况下,JSON 只能表示 Python 内置类型的子集,不能表示自定义的类;但 pickle 可以表示大量的 Python 数据类型(可以合理使用 Python 的对象内省功能自动地表示大多数类型,复杂情况可以通过实现 specific object APIs 来解决)。
- 不像pickle,对一个不信任的JSON进行反序列化的操作本身不会造成任意代码执行漏洞。
数据流格式
pickle
所使用的数据格式仅可用于 Python。这样做的好处是没有外部标准给该格式强加限制,比如 JSON 或 XDR(不能表示共享指针)标准;但这也意味着非 Python 程序可能无法重新读取 pickle 封存的 Python 对象。
默认情况下,pickle
格式使用相对紧凑的二进制来存储。如果需要让文件更小,可以高效地 压缩 由 pickle 封存的数据。
pickletools
模块包含了相应的工具用于分析 pickle
生成的数据流。pickletools
源码中包含了对 pickle 协议使用的操作码的大量注释。
当前共有 6 种不同的协议可用于封存操作。 使用的协议版本越高,读取所生成 pickle 对象所需的 Python 版本就要越新。
- v0 版协议是原始的“人类可读”协议,并且向后兼容早期版本的 Python。
- v1 版协议是较早的二进制格式,它也与早期版本的 Python 兼容。
- v2 版协议是在 Python 2.3 中引入的。它为存储 new-style class 提供了更高效的机制。欲了解有关第 2 版协议带来的改进,请参阅 PEP 307。
- v3 版协议是在 Python 3.0 中引入的。 它显式地支持
bytes
字节对象,不能使用 Python 2.x 解封。这是 Python 3.0-3.7 的默认协议。 - v4 版协议添加于 Python 3.4。它支持存储非常大的对象,能存储更多种类的对象,还包括一些针对数据格式的优化。它是Python 3.8使用的默认协议。有关第 4 版协议带来改进的信息,请参阅 PEP 3154。
- 第 5 版协议是在 Python 3.8 中加入的。 它增加了对带外数据的支持,并可加速带内数据处理。 请参阅 PEP 574 了解第 5 版协议所带来的改进的详情。
注解
序列化是一种比持久化更底层的概念,虽然 pickle
读取和写入的是文件对象,但它不处理持久对象的命名问题,也不处理对持久对象的并发访问(甚至更复杂)的问题。pickle
模块可以将复杂对象转换为字节流,也可以将字节流转换为具有相同内部结构的对象。处理这些字节流最常见的做法是将它们写入文件,但它们也可以通过网络发送或存储在数据库中。shelve
模块提供了一个简单的接口,用于在 DBM 类型的数据库文件上封存和解封对象。
模块接口
要序列化某个包含层次结构的对象,只需调用 dumps()
函数即可。同样,要反序列化数据流,可以调用 loads()
函数。但是,如果要对序列化和反序列化加以更多的控制,可以分别创建 Pickler
或 Unpickler
对象。
pickle
模块包含了以下常量:
pickle.HIGHEST_PROTOCOL
整数,可用的最高 协议版本。此值可以作为 协议 值传递给 dump()
和 dumps()
函数,以及 Pickler
的构造函数。
pickle.DEFAULT_PROTOCOL
整数,用于 pickle 数据的默认 协议版本。它可能小于 HIGHEST_PROTOCOL
。当前默认协议是 v4,它在 Python 3.4 中首次引入,与之前的版本不兼容。
在 3.0 版更改: 默认协议版本是 3。
在 3.8 版更改: 默认协议版本是 4。
pickle
模块提供了以下方法,让封存过程更加方便:
pickle.dump
(obj, file, protocol=None, **, fix_imports=True, buffer_callback=None*)
将对象 obj 封存以后的对象写入已打开的 file object file。它等同于 Pickler(file, protocol).dump(obj)
。
参数 file、protocol、fix_imports 和 buffer_callback 的含义与它们在 Pickler
的构造函数中的含义相同。
在 3.8 版更改: 加入了 buffer_callback 参数。
pickle.dumps
(obj, protocol=None, **, fix_imports=True, buffer_callback=None*)
将 obj 封存以后的对象作为 bytes
类型直接返回,而不是将其写入到文件。
参数 protocol、fix_imports 和 buffer_callback 的含义与它们在 Pickler
的构造函数中的含义相同。
在 3.8 版更改: 加入了 buffer_callback 参数。
pickle.load
(file, **, fix_imports=True, encoding=’ASCII’, errors=’strict’, buffers=None*)
从已打开的 file object文件 中读取封存后的对象,重建其中特定对象的层次结构并返回。它相当于 Unpickler(file).load()
。
Pickle 协议版本是自动检测出来的,所以不需要参数来指定协议。封存对象以外的其他字节将被忽略。
参数 file、fix_imports、encoding、errors、strict 和 buffers 的含义与它们在 Unpickler
的构造函数中的含义相同。
在 3.8 版更改: 加入了 buffers 参数。
pickle.loads
(data, /, **, fix_imports=True, encoding=”ASCII”, errors=”strict”, buffers=None*)
重建并返回一个对象的封存表示形式 data 的对象层级结构。 data 必须为 bytes-like object。
Pickle 协议版本是自动检测出来的,所以不需要参数来指定协议。封存对象以外的其他字节将被忽略。
参数 file、fix_imports、encoding、errors、strict 和 buffers 的含义与它们在 Unpickler
的构造函数中的含义相同。
在 3.8 版更改: 加入了 buffers 参数。
pickle
模块定义了以下 3 个异常:
exceptionpickle.PickleError
其他 pickle 异常的基类。它是 Exception
的一个子类。
exceptionpickle.PicklingError
当 Pickler
遇到无法解封的对象时抛出此错误。它是 PickleError
的子类。
exceptionpickle.UnpicklingError
当解封出错时抛出此异常,例如数据损坏或对象不安全。它是 PickleError
的子类。
注意,解封时可能还会抛出其他异常,包括(但不限于) AttributeError、EOFError、ImportError 和 IndexError。
pickle
模块包含了 3 个类,Pickler
、Unpickler
和 PickleBuffer
:
classpickle.Pickler
(file, protocol=None, **, fix_imports=True, buffer_callback=None*)
它接受一个二进制文件用于写入 pickle 数据流。
可选参数 protocol 是一个整数,告知 pickler 使用指定的协议,可选择的协议范围从 0 到 HIGHEST_PROTOCOL
。如果没有指定,这一参数默认值为 DEFAULT_PROTOCOL
。指定一个负数就相当于指定 HIGHEST_PROTOCOL
。
参数 file 必须有一个 write() 方法,该 write() 方法要能接收字节作为其唯一参数。因此,它可以是一个打开的磁盘文件(用于写入二进制内容),也可以是一个 io.BytesIO
实例,也可以是满足这一接口的其他任何自定义对象。
如果 fix_imports 为 True 且 protocol 小于 3,pickle 将尝试将 Python 3 中的新名称映射到 Python 2 中的旧模块名称,因此 Python 2 也可以读取封存的数据流。
如果 buffer_callback 为 None(默认情况),缓冲区视图(buffer view)将会作为 pickle 流的一部分被序列化到 file 中。
如果 buffer_callback 不为 None,那它可以用缓冲区视图调用任意次。如果某次调用返回了 False 值(例如 None),则给定的缓冲区是 带外的,否则缓冲区是带内的(例如保存在了 pickle 流里面)。
如果 buffer_callback 不是 None 且 protocol 是 None 或小于 5,就会出错。
在 3.8 版更改: 加入了 buffer_callback 参数。
dump
(obj)将 obj 封存后的内容写入已打开的文件对象,该文件对象已经在构造函数中指定。
persistent_id
(obj)默认无动作,子类继承重载时使用。
如果
persistent_id()
返回None
,obj 会被照常 pickle。如果返回其他值,Pickler
会将这个函数的返回值作为 obj 的持久化 ID(Pickler 本应得到序列化数据流并将其写入文件,若此函数有返回值,则得到此函数的返回值并写入文件)。这个持久化 ID 的解释应当定义在Unpickler.persistent_load()
中(该方法定义还原对象的过程,并返回得到的对象)。注意,persistent_id()
的返回值本身不能拥有持久化 ID。dispatch_table
Pickler 对象的 dispatch 表是
copyreg.pickle()
中用到的 reduction 函数 的注册。dispatch 表本身是一个 class 到其 reduction 函数的映射键值对。一个 reduction 函数只接受一个参数,就是其关联的 class,函数行为应当遵守__reduce__()
接口规范。Pickler 对象默认并没有
dispatch_table
属性,该对象默认使用copyreg
模块中定义的全局 dispatch 表。如果要为特定 Pickler 对象自定义序列化过程,可以将dispatch_table
属性设置为类字典对象(dict-like object)。另外,如果Pickler
的子类设置了dispatch_table
属性,则该子类的实例会使用这个表作为默认的 dispatch 表。3.3 新版功能.
reducer_override
(self, obj)可以在
Pickler
的子类中定义的特殊 reducer。此方法的优先级高于dispatch_table
中的任何 reducer。它应该与__reduce__()
方法遵循相同的接口,它也可以返回NotImplemented
,这将使用dispatch_table
里注册的 reducer 来封存obj
。3.8 新版功能.
fast
已弃用。设为 True 则启用快速模式。快速模式禁用了“备忘录” (memo) 的使用,即不生成多余的 PUT 操作码来加快封存过程。不应将其与自指 (self-referential) 对象一起使用,否则将导致
Pickler
无限递归。如果需要进一步提高 pickle 的压缩率,请使用
pickletools.optimize()
。
classpickle.Unpickler
(file, **, fix_imports=True, encoding=’ASCII’, errors=’strict’, buffers=None*)
它接受一个二进制文件用于读取 pickle 数据流。
Pickle 协议版本是自动检测出来的,所以不需要参数来指定协议。
参数 file 必须有三个方法,read() 方法接受一个整数参数,readinto() 方法接受一个缓冲区作为参数,readline() 方法不需要参数,这与 io.BufferedIOBase
里定义的接口是相同的。因此 file 可以是一个磁盘上用于二进制读取的文件,也可以是一个 io.BytesIO
实例,也可以是满足这一接口的其他任何自定义对象。
可选的参数是 fix_imports, encoding 和 errors*,用于控制由Python 2 生成的 pickle 流的兼容性。如果 *fix_imports 为 True,则 pickle 将尝试将旧的 Python 2 名称映射到 Python 3 中对应的新名称。encoding 和 errors 参数告诉 pickle 如何解码 Python 2 存储的 8 位字符串实例;这两个参数默认分别为 ‘ASCII’ 和 ‘strict’。encoding 参数可置为 ‘bytes’ 来将这些 8 位字符串实例读取为字节对象。读取 NumPy array 和 Python 2 存储的 datetime
、date
和 time
实例时,请使用 encoding='latin1'
。
如果 buffers 为 None(默认值),则反序列化所需的所有数据都必须包含在 pickle 流中。这意味着在实例化 Pickler
时(或调用 dump()
或 dumps()
时),参数 buffer_callback 为 None。
如果 buffers 不为 None,则每次 pickle 流引用 带外 缓冲区视图时,消耗的对象都应该是可迭代的启用缓冲区的对象。这样的缓冲区应该按顺序地提供给 Pickler 对象的 buffer_callback 方法。
在 3.8 版更改: 加入了 buffers 参数。
load
()从构造函数中指定的文件对象里读取封存好的对象,重建其中特定对象的层次结构并返回。封存对象以外的其他字节将被忽略。
persistent_load
(pid)默认抛出
UnpicklingError
异常。如果定义了此方法,
persistent_load()
应当返回持久化 ID pid 所指定的对象。 如果遇到无效的持久化 ID,则应当引发UnpicklingError
。find_class
(module, name)如有必要,导入 module 模块并返回其中名叫 name 的对象,其中 module 和 name 参数都是
str
对象。注意,不要被这个函数的名字迷惑,find_class()
同样可以用来导入函数。子类可以重载此方法,来控制加载对象的类型和加载对象的方式,从而尽可能降低安全风险。
引发一个 审计事件
pickle.find_class
附带参数module
、name
。
classpickle.PickleBuffer
(buffer)
缓冲区的包装器 (wrapper),缓冲区中包含着可封存的数据。buffer 必须是一个 buffer-providing 对象,比如 bytes-like object 或多维数组。
PickleBuffer
本身就可以生成缓冲区对象,因此可以将其传递给需要缓冲区生成器的其他 API,比如 memoryview
。
PickleBuffer
对象只能用 pickle 版本 5 及以上协议进行序列化。它们符合 带外序列化 的条件。
3.8 新版功能.
raw
()返回该缓冲区底层内存区域的
memoryview
。 返回的对象是一维的、C 连续布局的 memoryview,格式为B
(无符号字节)。 如果缓冲区既不是 C 连续布局也不是 Fortran 连续布局的,则抛出BufferError
异常。release
()释放由 PickleBuffer 占用的底层缓冲区。
可以被封存/解封的对象
下列类型可以被封存:
None
、True
和False
- 整数、浮点数、复数
- str、byte、bytearray
- 只包含可封存对象的集合,包括 tuple、list、set 和 dict
- 定义在模块最外层的函数(使用
def
定义,lambda
函数则不可以) - 定义在模块最外层的内置函数
- 定义在模块最外层的类
- 某些类实例,这些类的
__dict__
属性值或__getstate__()
函数的返回值可以被封存。
尝试封存不能被封存的对象会抛出 PicklingError
异常,异常发生时,可能有部分字节已经被写入指定文件中。尝试封存递归层级很深的对象时,可能会超出最大递归层级限制,此时会抛出 RecursionError
异常,可以通过 sys.setrecursionlimit()
调整递归层级,不过请谨慎使用这个函数,因为可能会导致解释器崩溃。
注意,函数(内置函数或用户自定义函数)在被封存时,引用的是函数全名。这意味着只有函数所在的模块名,与函数名会被封存,函数体及其属性不会被封存。因此,在解封的环境中,函数所属的模块必须是可以被导入的,而且模块必须包含这个函数被封存时的名称,否则会抛出异常.
同样的,类也只封存名称,所以在解封环境中也有和函数相同的限制。注意,类体及其数据不会被封存,所以在下面的例子中类属性 attr
不会存在于解封后的环境中:
classFoo: attr ='A class attribute'picklestring = pickle.dumps(Foo)
这些限制决定了为什么必须在一个模块的最外层定义可封存的函数和类。
类似的,在封存类的实例时,其类体和类数据不会跟着实例一起被封存,只有实例数据会被封存。这样设计是有目的的,在将来修复类中的错误、给类增加方法之后,仍然可以载入原来版本类实例的封存数据来还原该实例。如果你准备长期使用一个对象,可能会同时存在较多版本的类体,可以为对象添加版本号,这样就可以通过类的 __setstate__()
方法将老版本转换成新版本。
封存类实例
在本节中,我们描述了可用于定义、自定义和控制如何封存和解封类实例的通用流程。
通常,使一个实例可被封存不需要附加任何代码。Pickle 默认会通过 Python 的内省机制获得实例的类及属性。而当实例解封时,它的 __init__()
方法通常 不会 被调用。其默认动作是:先创建一个未初始化的实例,然后还原其属性,下面的代码展示了这种行为的实现机制:
def save(obj):
return(obj.__class__, obj.__dict__)
def load(cls, attributes):
obj = cls.__new__(cls)
obj.__dict__.update(attributes)
return obj
类可以改变默认行为,只需定义以下一种或几种特殊方法:
object.__getnewargs_ex__
()
对于使用第 2 版或更高版协议的 pickle,实现了 __getnewargs_ex__()
方法的类可以控制在解封时传给 __new__()
方法的参数。本方法必须返回一对 (args, kwargs)
用于构建对象,其中 args 是表示位置参数的 tuple,而 kwargs 是表示命名参数的 dict。它们会在解封时传递给 __new__()
方法。
如果类的 __new__()
方法只接受关键字参数,则应当实现这个方法。否则,为了兼容性,更推荐实现 __getnewargs__()
方法。
在 3.6 版更改: __getnewargs_ex__()
现在可用于第 2 和第 3 版协议。
object.__getnewargs__
()
这个方法与上一个 __getnewargs_ex__()
方法类似,但仅支持位置参数。它要求返回一个 tuple 类型的 args
,用于解封时传递给 __new__()
方法。
如果定义了 __getnewargs_ex__()
,那么 __getnewargs__()
就不会被调用。
在 3.6 版更改: 在 Python 3.6 前,第 2、3 版协议会调用 __getnewargs__()
,更高版本协议会调用 __getnewargs_ex__()
。
object.__getstate__
()
类还可以进一步控制其实例的封存过程。如果类定义了 __getstate__()
,它就会被调用,其返回的对象是被当做实例内容来封存的,否则封存的是实例的 dict。如果 __getstate__()
未定义,实例的 __dict__
会被照常封存。
object.__setstate__
(state)
当解封时,如果类定义了 __setstate__()
,就会在已解封状态下调用它。此时不要求实例的 state 对象必须是 dict。没有定义此方法的话,先前封存的 state 对象必须是 dict,且该 dict 内容会在解封时赋给新实例的 dict。
注解
如果 __getstate__()
返回 False,那么在解封时就不会调用 __setstate__()
方法。
注解
在解封时,实例的某些方法例如 __getattr__()
, __getattribute__()
或 __setattr__()
可能会被调用。 由于这些方法可能要求某些内部不变量为真值,因此该类型应当实现 __new__()
以建立这样的不变量,因为当解封一个实例时 __init__()
并不会被调用。
可以看出,其实 pickle 并不直接调用上面的几个函数。事实上,这几个函数是复制协议的一部分,它们实现了 __reduce__()
这一特殊接口。复制协议提供了统一的接口,用于在封存或复制对象的过程中取得所需数据.
尽管这个协议功能很强,但是直接在类中实现 __reduce__()
接口容易产生错误。因此,设计类时应当尽可能的使用高级接口(比如 __getnewargs_ex__()
、__getstate__()
和 __setstate__()
)。后面仍然可以看到直接实现 __reduce__()
接口的状况,可能别无他法,可能为了获得更好的性能,或者两者皆有之。
object.__reduce__
()
该接口当前定义如下。__reduce__()
方法不带任何参数,并且应返回字符串或最好返回一个元组(返回的对象通常称为“reduce 值”)。
如果返回字符串,该字符串会被当做一个全局变量的名称。它应该是对象相对于其模块的本地名称,pickle 模块会搜索模块命名空间来确定对象所属的模块。这种行为常在单例模式使用。
如果返回的是元组,则应当包含 2 到 6 个元素,可选元素可以省略或设置为 None
。每个元素代表的意义如下:
一个可调用对象,该对象会在创建对象的最初版本时调用。
可调用对象的参数,是一个元组。如果可调用对象不接受参数,必须提供一个空元组。
可选元素,用于表示对象的状态,将被传给前述的
__setstate__()
方法。 如果对象没有此方法,则这个元素必须是字典类型,并会被添加至__dict__
属性中。可选元素,一个返回连续项的迭代器(而不是序列)。这些项会被
obj.append(item)
逐个加入对象,或被obj.extend(list_of_items)
批量加入对象。这个元素主要用于 list 的子类,也可以用于那些正确实现了append()
和extend()
方法的类。(具体是使用append()
还是extend()
取决于 pickle 协议版本以及待插入元素的项数,所以这两个方法必须同时被类支持。)可选元素,一个返回连续键值对的迭代器(而不是序列)。这些键值对将会以
obj[key] = value
的方式存储于对象中。该元素主要用于 dict 子类,也可以用于那些实现了__setitem__()
的类。可选元素,一个带有
(obj, state)
签名的可调用对象。该可调用对象允许用户以编程方式控制特定对象的状态更新行为,而不是使用obj
的静态__setstate__()
方法。如果此处不是None
,则此可调用对象的优先级高于obj
的__setstate__()
。3.8 新版功能: 新增了元组的第 6 项,可选元素
(obj, state)
。
object.__reduce_ex__
(protocol)
作为替代选项,也可以实现 __reduce_ex__()
方法。 此方法的唯一不同之处在于它应接受一个整型参数用于指定协议版本。 如果定义了这个函数,则会覆盖 __reduce__()
的行为。 此外,__reduce__()
方法会自动成为扩展版方法的同义词。 这个函数主要用于为以前的 Python 版本提供向后兼容的 reduce 值。
持久化外部对象
为了获取对象持久化的利益, pickle
模块支持引用已封存数据流之外的对象。 这样的对象是通过一个持久化 ID 来引用的,它应当是一个由字母数字类字符组成的字符串 (对于第 0 版协议) 或是一个任意对象 (用于任意新版协议)。
pickle
模块不提供对持久化 ID 的解析工作,它将解析工作分配给用户定义的方法,分别是 pickler 中的 persistent_id()
方法和 unpickler 中的 persistent_load()
方法。
要通过持久化 ID 将外部对象封存,必须在 pickler 中实现 persistent_id()
方法,该方法接受需要被封存的对象作为参数,返回一个 None
或返回该对象的持久化 ID。如果返回 None
,该对象会被按照默认方式封存为数据流。如果返回字符串形式的持久化 ID,则会封存这个字符串并加上一个标记,这样 unpickler 才能将其识别为持久化 ID。
要解封外部对象,Unpickler 必须实现 persistent_load()
方法,接受一个持久化 ID 对象作为参数并返回一个引用的对象。
下面是一个全面的例子,展示了如何使用持久化 ID 来封存外部对象。
# Simple example presenting how persistent ID can be used to pickle
# external objects by reference.
import pickle
import sqlite3
from collections import namedtuple
# Simple class representing a record in our database.
MemoRecord= namedtuple("MemoRecord","key, task")
classDBPickler(pickle.Pickler):
def persistent_id(self, obj):
# Instead of pickling MemoRecord as a regular class instance, we emit a
# persistent ID.
if isinstance(obj,MemoRecord):
# Here, our persistent ID is simply a tuple, containing a tag and a
# key, which refers to a specific record in the database.
return("MemoRecord", obj.key)
else:
# If obj does not have a persistent ID, return None. This means obj
# needs to be pickled as usual.
returnNone
classDBUnpickler(pickle.Unpickler):
def __init__(self, file, connection):
super().__init__(file)
self.connection = connection
def persistent_load(self, pid):
# This method is invoked whenever a persistent ID is encountered.
# Here, pid is the tuple returned by DBPickler.
cursor =self.connection.cursor()
type_tag, key_id = pid
if type_tag =="MemoRecord":
# Fetch the referenced record from the database and return it.
cursor.execute("SELECT * FROM memos WHERE key=?",(str(key_id),))
key, task = cursor.fetchone()
returnMemoRecord(key, task)
else:
# Always raises an error if you cannot return the correct object.
# Otherwise, the unpickler will think None is the object referenced
# by the persistent ID.
raise pickle.UnpicklingError("unsupported persistent object")
def main():
import io
import pprint
# Initialize and populate our database.
conn = sqlite3.connect(":memory:")
cursor = conn.cursor()
cursor.execute("CREATE TABLE memos(key INTEGER PRIMARY KEY, task TEXT)")
tasks =(
'give food to fish',
'prepare group meeting',
'fight with a zebra',
)
for task in tasks:
cursor.execute("INSERT INTO memos VALUES(NULL, ?)",(task,))
# Fetch the records to be pickled.
cursor.execute("SELECT * FROM memos")
memos =[MemoRecord(key, task)for key, task in cursor]
# Save the records using our custom DBPickler.
file = io.BytesIO()
DBPickler(file).dump(memos)
print("Pickled records:")
pprint.pprint(memos)
# Update a record, just for good measure.
cursor.execute("UPDATE memos SET task='learn italian' WHERE key=1")
# Load the records from the pickle data stream.
file.seek(0)
memos =DBUnpickler(file, conn).load()
print("Unpickled records:")
pprint.pprint(memos)
if __name__ =='__main__':
main()
Dispatch 表
如果想对某些类进行自定义封存,而又不想在类中增加用于封存的代码,就可以创建带有特殊 dispatch 表的 pickler。
在 copyreg
模块的 copyreg.dispatch_table
中定义了全局 dispatch 表。因此,可以使用 copyreg.dispatch_table
修改后的副本作为自有 dispatch 表。
例如
f = io.BytesIO()
p = pickle.Pickler(f)
p.dispatch_table = copyreg.dispatch_table.copy()
p.dispatch_table[SomeClass]= reduce_SomeClass
创建了一个带有自有 dispatch 表的 pickle.Pickler
实例,它可以对 SomeClass
类进行特殊处理。另外,下列代码
classMyPickler(pickle.Pickler):
dispatch_table = copyreg.dispatch_table.copy()
dispatch_table[SomeClass]= reduce_SomeClass
f = io.BytesIO()
p =MyPickler(f)
完成了相同的操作,但所有 MyPickler
的实例都会共用同一份 dispatch 表。使用 copyreg
模块实现的等效代码是
copyreg.pickle(SomeClass, reduce_SomeClass)
f = io.BytesIO()
p = pickle.Pickler(f)
处理有状态的对象
下面的示例展示了如何修改类在封存时的行为。其中 TextReader
类打开了一个文本文件,每次调用其 readline()
方法则返回行号和该行的字符。 在封存这个 TextReader
的实例时,除了 文件对象,其他属性都会被保存。 当解封实例时,需要重新打开文件,然后从上次的位置开始继续读取。实现这些功能需要实现 __setstate__()
和 __getstate__()
方法。
classTextReader:
"""Print and number lines in a text file."""
def __init__(self, filename):
self.filename = filename
self.file = open(filename)
self.lineno =0
def readline(self):
self.lineno +=1
line =self.file.readline()
ifnot line:
returnNone
if line.endswith('\n'):
line = line[:-1]
return"%i: %s"%(self.lineno, line)
def __getstate__(self):
# Copy the object's state from self.__dict__ which contains
# all our instance attributes. Always use the dict.copy()
# method to avoid modifying the original state.
state =self.__dict__.copy()
# Remove the unpicklable entries.
del state['file']
return state
def __setstate__(self, state):
# Restore instance attributes (i.e., filename and lineno).
self.__dict__.update(state)
# Restore the previously opened file's state. To do so, we need to
# reopen it and read from it until the line count is restored.
file = open(self.filename)
for _ in range(self.lineno):
file.readline()
# Finally, save the file.
self.file = file
使用方法如下所示:
>>> reader =TextReader("hello.txt")
>>> reader.readline()
'1: Hello world!'
>>> reader.readline()
'2: I am line number two.'
>>> new_reader = pickle.loads(pickle.dumps(reader))
>>> new_reader.readline()
'3: Goodbye!'
类型,函数和其他对象的自定义归约
3.8 新版功能.
有时,dispatch_table
可能不够灵活。 特别是当我们想要基于对象类型以外的其他规则来对封存进行定制,或是当我们想要对函数和类的封存进行定制的时候。
对于那些情况,可能要基于 Pickler
类进行子类化并实现 reducer_override()
方法。 此方法可返回任意的归约元组 (参见 __reduce__()
)。 它也可以选择返回 NotImplemented
来回退到传统行为。
如果同时定义了 dispatch_table
和 reducer_override()
,则 reducer_override()
方法具有优先权。
注解
出于性能理由,可能不会为以下对象调用 reducer_override()
: None
, True
, False
, 以及 int
, float
, bytes
, str
, dict
, set
, frozenset
, list
和 tuple
的具体实例。
以下是一个简单的例子,其中我们允许封存并重新构建一个给定的类:
import io
import pickle
classMyClass:
my_attribute =1
classMyPickler(pickle.Pickler):
def reducer_override(self, obj):
"""Custom reducer for MyClass."""
if getattr(obj,"__name__",None)=="MyClass":
return type,(obj.__name__, obj.__bases__,
{'my_attribute': obj.my_attribute})
else:
# For any other object, fallback to usual reduction
returnNotImplemented
f = io.BytesIO()
p =MyPickler(f)
p.dump(MyClass)
delMyClass
unpickled_class = pickle.loads(f.getvalue())
assert isinstance(unpickled_class, type)
assert unpickled_class.__name__ =="MyClass"
assert unpickled_class.my_attribute ==1
外部缓冲区
3.8 新版功能.
在某些场景中,pickle
模块会被用来传输海量的数据。 因此,最小化内存复制次数以保证性能和节省资源是很重要的。 但是 pickle
模块的正常运作会将图类对象结构转换为字节序列流,因此在本质上就要从封存流中来回复制数据。
如果 provider (待传输对象类型的实现) 和 consumer (通信系统的实现) 都支持 pickle 第 5 版或更高版本所提供的外部传输功能,则此约束可以被撤销。
提供方 API
大的待封存数据对象必须实现协议 5 及以上版本专属的 __reduce_ex__()
方法,该方法将为任意大的数据返回一个 PickleBuffer
实例(而不是 bytes
对象等)。
PickleBuffer
对象会 表明 底层缓冲区可被用于外部数据传输。 那些对象仍将保持与 pickle
模块的正常用法兼容。 但是,使用方也可以选择告知 pickle
它们将自行处理那些缓冲区。
使用方 API
当序列化一个对象图时,通信系统可以启用对所生成 PickleBuffer
对象的定制处理。
发送端需要传递 buffer_callback 参数到 Pickler
(或是到 dump()
或 dumps()
函数),该回调函数将在封存对象图时附带每个所生成的 PickleBuffer
被调用。 由 buffer_callback 所累积的缓冲区的数据将不会被拷贝到 pickle 流,而是仅插入一个简单的标记。
接收端需要传递 buffers 参数到 Unpickler
(或是到 load()
或 loads()
函数),其值是一个由缓冲区组成的可迭代对象,它会被传递给 buffer_callback*。 该可迭代对象应当按其被传递给 *buffer_callback 时的顺序产生缓冲区。 这些缓冲区将提供对象重构造器所期望的数据,对这些数据的封存产生了原本的 PickleBuffer
对象。
在发送端和接受端之间,通信系统可以自由地实现它自己用于外部缓冲区的传输机制。 潜在的优化包括使用共享内存或基于特定数据类型的压缩等。
示例
下面是一个小例子,在其中我们实现了一个 bytearray
的子类,能够用于外部缓冲区封存:
classZeroCopyByteArray(bytearray):
def __reduce_ex__(self, protocol):
if protocol >=5:
return type(self)._reconstruct,(PickleBuffer(self),),None
else:
# PickleBuffer is forbidden with pickle protocols <= 4.
return type(self)._reconstruct,(bytearray(self),)
@classmethod
def _reconstruct(cls, obj):
with memoryview(obj)as m:
# Get a handle over the original buffer object
obj = m.obj
if type(obj)is cls:
# Original buffer object is a ZeroCopyByteArray, return it
# as-is.
return obj
else:
return cls(obj)
重构造器 (_reconstruct
类方法) 会在缓冲区的提供对象具有正确类型时返回该对象。 在此小示例中这是模拟零拷贝行为的便捷方式。
在使用方,我们可以按通常方式封存那些对象,它们在反序列化时将提供原始对象的一个副本:
b =ZeroCopyByteArray(b"abc")
data = pickle.dumps(b, protocol=5)
new_b = pickle.loads(data)
print(b == new_b)# True
print(b is new_b)# False: a copy was made
但是如果我们传入 buffer_callback 然后在反序列化时给回累积的缓冲区,我们就能够取回原始对象:
b =ZeroCopyByteArray(b"abc")
buffers =[]
data = pickle.dumps(b, protocol=5, buffer_callback=buffers.append)
new_b = pickle.loads(data, buffers=buffers)
print(b == new_b)# True
print(b is new_b)# True: no copy was made
这个例子受限于 bytearray
会自行分配内存这一事实:你无法基于另一个对象的内存创建 bytearray
的实例。 但是,第三方数据类型例如 NumPy 数组则没有这种限制,允许在单独进程或系统间传输时使用零拷贝的封存(或是尽可能少地拷贝) 。
参见
PEP 574 — 带有外部数据缓冲区的 pickle 协议 5
限制全局变量
默认情况下,解封将会导入在 pickle 数据中找到的任何类或函数。 对于许多应用来说,此行为是不可接受的,因为它会允许解封器导入并发起调用任意代码。 只须考虑当这个手工构建的 pickle 数据流被加载时会做什么:
>>>import pickle
>>> pickle.loads(b"cos\nsystem\n(S'echo hello world'\ntR.")
hello world
0
在这个例子里,解封器导入 os.system()
函数然后应用字符串参数 “echo hello world”。 虽然这个例子不具攻击性,但是不难想象别人能够通过此方式对你的系统造成损害。
出于这样的理由,你可能会希望通过定制 Unpickler.find_class()
来控制要解封的对象。 与其名称所提示的不同,Unpickler.find_class()
会在执行对任何全局对象(例如一个类或一个函数)的请求时被调用。 因此可以完全禁止全局对象或是将它们限制在一个安全的子集中。
下面的例子是一个解封器,它只允许某一些安全的来自 builtins
模块的类被加载:
import builtins
import io
import pickle
safe_builtins ={
'range',
'complex',
'set',
'frozenset',
'slice',
}
classRestrictedUnpickler(pickle.Unpickler):
def find_class(self,module, name):
# Only allow safe classes from builtins.
ifmodule=="builtins"and name in safe_builtins:
return getattr(builtins, name)
# Forbid everything else.
raise pickle.UnpicklingError("global '%s.%s' is forbidden"%
(module, name))
def restricted_loads(s):
"""Helper function analogous to pickle.loads()."""
returnRestrictedUnpickler(io.BytesIO(s)).load()
我们这个解封器的一个示例用法所达成的目标:
>>> restricted_loads(pickle.dumps([1,2, range(15)]))
[1,2, range(0,15)]
>>> restricted_loads(b"cos\nsystem\n(S'echo hello world'\ntR.")
Traceback(most recent call last):
...
pickle.UnpicklingError:global'os.system'is forbidden
>>> restricted_loads(b'cbuiltins\neval\n'
... b'(S\'getattr(__import__("os"), "system")'
... b'("echo hello world")\'\ntR.')
Traceback(most recent call last):
...
pickle.UnpicklingError:global'builtins.eval'is forbidden
正如我们这个例子所显示的,对于允许解封的对象你必须要保持谨慎。 因此如果要保证安全,你可以考虑其他选择例如 xmlrpc.client
中的编组 API 或是第三方解决方案。
性能
较新版本的 pickle 协议(第 2 版或更高)具有针对某些常见特性和内置类型的高效二进制编码格式。 此外,pickle
模块还拥有一个以 C 编写的透明优化器。
例子
对于最简单的代码,请使用 dump()
和 load()
函数。
import pickle
# An arbitrary collection of objects supported by pickle.
data ={
'a':[1,2.0,3,4+6j],
'b':("character string", b"byte string"),
'c':{None,True,False}
}
with open('data.pickle','wb')as f:
# Pickle the 'data' dictionary using the highest protocol available.
pickle.dump(data, f, pickle.HIGHEST_PROTOCOL)
以下示例读取之前封存的数据。
import pickle
with open('data.pickle','rb')as f:
# The protocol version used is detected automatically, so we do not
# have to specify it.
data = pickle.load(f)
copyreg
—- 注册配合 pickle
模块使用的函数
源代码: Lib/copyreg.py
copyreg
模块提供了可在封存特定对象时使用的一种定义函数方式。 pickle
和 copy
模块会在封存/拷贝特定对象时使用这些函数。 此模块提供了非类对象构造器的相关配置信息。 这样的构造器可以是工厂函数或类实例。
copyreg.constructor
(object)
将 object 声明为一个有效的构造器。 如果 object 是不可调用的(因而不是一个有效的构造器)则会引发 TypeError
。
copyreg.pickle
(type, function, constructor=None)
声明该 function 应当被用作 type 类型对象的“归约函数”。 function 应当返回字符串或包含两到三个元素的元组。
如果提供了可选的 constructor 形参,它应当是一个可用来重建相应对象的可调用对象,在调用该对象时应传入由 function 所返回的参数元组。 如果 object 是一个类或 constructor 是不可调用的则将引发 TypeError
。
请查看 pickle
模块了解 function 和 constructor 所要求的接口的详情。 请注意一个 pickler 对象或 pickle.Pickler
的子类的 dispatch_table
属性也可以被用来声明归约函数。
示例
以下示例将会显示如何注册一个封存函数,以及如何来使用它:
>>> import copyreg, copy, pickle
>>> class C:
... def __init__(self, a):
... self.a = a
...
>>> def pickle_c(c):
... print("pickling a C instance...")
... return C, (c.a,)
...
>>> copyreg.pickle(C, pickle_c)
>>> c = C(1)
>>> d = copy.copy(c)
pickling a C instance...
>>> p = pickle.dumps(c)
pickling a C instance...
shelve
—- Python 对象持久化
源代码: Lib/shelve.py
“Shelf” 是一种持久化的类似字典的对象。 与 “dbm” 数据库的区别在于 Shelf 中的值(不是键!)实际上可以为任意 Python 对象 —- 即 pickle
模块能够处理的任何东西。 这包括大部分类实例、递归数据类型,以及包含大量共享子对象的对象。 键则为普通的字符串。
shelve.open
(filename, flag=’c’, protocol=None, writeback=False)
打开一个持久化字典。 filename 指定下层数据库的基准文件名。 作为附带效果,会为 filename 添加一个扩展名并且可能创建更多的文件。 默认情况下,下层数据库会以读写模式打开。 可选的 flag 形参具有与 dbm.open()
flag 形参相同的含义。
在默认情况下,会使用以 pickle.DEFAULT_PROTOCOL
创建的 pickle 来序列化值。 pickle 协议的版本可通过 protocol 形参来指定。
由于 Python 语义的限制,Shelf 对象无法确定一个可变的持久化字典条目在何时被修改。 默认情况下 只有 在被修改对象再赋值给 shelf 时才会写入该对象。 如果可选的 writeback 形参设为 True
,则所有被访问的条目都将在内存中被缓存,并会在 sync()
和 close()
时被写入;这可以使得对持久化字典中可变条目的修改更方便,但是如果访问的条目很多,这会消耗大量内存作为缓存,并会使得关闭操作变得非常缓慢,因为所有被访问的条目都需要写回到字典(无法确定被访问的条目中哪个是可变的,也无法确定哪个被实际修改了)。
在 3.10 版更改: pickle.DEFAULT_PROTOCOL
现在会被用作默认的 pickle 协议。
注解
请不要依赖于 Shelf 的自动关闭功能;当你不再需要时应当总是显式地调用 close()
,或者使用 shelve.open()
作为上下文管理器:
with shelve.open('spam') as db:
db['eggs'] = 'eggs'
警告
由于 shelve
模块需要 pickle
的支持,因此从不可靠的来源载入 shelf 是不安全的。 与 pickle 一样,载入 Shelf 时可以执行任意代码。
Shelf 对象支持字典所支持的大多数方法和运算(除了拷贝、构造器以及 |
和 |=
运算符)。 这样就能方便地将基于字典的脚本转换为要求持久化存储的脚本。
额外支持的两个方法:
Shelf.sync
()
如果 Shelf 打开时将 writeback 设为 True
则写回缓存中的所有条目。 如果可行还会清空缓存并将持久化字典同步到磁盘。 此方法会在使用 close()
关闭 Shelf 时自动被调用。
Shelf.close
()
同步并关闭持久化 dict 对象。 对已关闭 Shelf 的操作将失败并引发 ValueError
。
限制
- 可选择使用哪种数据库包 (例如
dbm.ndbm
或dbm.gnu
) 取决于支持哪种接口。 因此使用dbm
直接打开数据库是不安全的。 如果使用了dbm
,数据库同样会(不幸地)受限于它 —- 这意味着存储在数据库中的(封存形式的)对象尺寸应当较小,并且在少数情况下键冲突有可能导致数据库拒绝更新。 shelve
模块不支持对 Shelf 对象的 并发 读/写访问。 (多个同时读取访问则是安全的。) 当一个程序打开一个 shelve 对象来写入时,不应再有其他程序同时打开它来读取或写入。 Unix 文件锁定可被用来解决此问题,但这在不同 Unix 版本上会存在差异,并且需要有关所用数据库实现的细节知识。
class shelve.Shelf
(dict, protocol=None, writeback=False, keyencoding=’utf-8’)
collections.abc.MutableMapping
的一个子类,它会将封存的值保存在 dict 对象中。
在默认情况下,会使用以 pickle.DEFAULT_PROTOCOL
创建的 pickle 来序列化值。 pickle 协议的版本可通过 protocol 形参来指定。
如果 writeback 形参为 True
,对象将为所有访问过的条目保留缓存并在同步和关闭时将它们写回到 dict。 这允许对可变的条目执行自然操作,但是会消耗更多内存并让同步和关闭花费更长时间。
keyencoding 形参是在下层字典被使用之前用于编码键的编码格式。
Shelf
对象还可以被用作上下文管理器,在这种情况下它将在 with
语句块结束时自动被关闭。
在 3.2 版更改: 添加了 keyencoding 形参;之前,键总是使用 UTF-8 编码。
在 3.4 版更改: 添加了上下文管理器支持。
在 3.10 版更改: pickle.DEFAULT_PROTOCOL
现在会被用作默认的 pickle 协议。
class shelve.BsdDbShelf
(dict, protocol=None, writeback=False, keyencoding=’utf-8’)
Shelf
的一个子类,将 first()
, next()
, previous()
, last()
和 set_location()
对外公开,在来自 pybsddb 的第三方 bsddb
模块中可用,但在其他数据库模块中不可用。 传给构造器的 dict 对象必须支持这些方法。 这通常是通过调用 bsddb.hashopen()
, bsddb.btopen()
或 bsddb.rnopen()
之一来完成的。 可选的 protocol, writeback 和 keyencoding 形参具有与 Shelf
类相同的含义。
class shelve.DbfilenameShelf
(filename, flag=’c’, protocol=None, writeback=False)
Shelf
的一个子类,它接受一个 filename 而非字典类对象。 下层文件将使用 dbm.open()
来打开。 默认情况下,文件将以读写模式打开。 可选的 flag 形参具有与 open()
函数相同的含义。 可选的 protocol 和 writeback 形参具有与 Shelf
类相同的含义。
示例
对接口的总结如下 (key
为字符串,data
为任意对象):
import shelve
d = shelve.open(filename) # open -- file may get suffix added by low-level
# library
d[key] = data # store data at key (overwrites old data if
# using an existing key)
data = d[key] # retrieve a COPY of data at key (raise KeyError
# if no such key)
del d[key] # delete data stored at key (raises KeyError
# if no such key)
flag = key in d # true if the key exists
klist = list(d.keys()) # a list of all existing keys (slow!)
# as d was opened WITHOUT writeback=True, beware:
d['xx'] = [0, 1, 2] # this works as expected, but...
d['xx'].append(3) # *this doesn't!* -- d['xx'] is STILL [0, 1, 2]!
# having opened d without writeback=True, you need to code carefully:
temp = d['xx'] # extracts the copy
temp.append(5) # mutates the copy
d['xx'] = temp # stores the copy right back, to persist it
# or, d=shelve.open(filename,writeback=True) would let you just code
# d['xx'].append(5) and have it work as expected, BUT it would also
# consume more memory and make the d.close() operation slower.
d.close() # close it
marshal
—- 内部 Python 对象序列化
此模块包含一此能以二进制格式来读写 Python 值的函数。 这种格式是 Python 专属的,但是独立于特定的机器架构(即你可以在一台 PC 上写入某个 Python 值,将文件传到一台 Sun 上并在那里读取它)。 这种格式的细节有意不带文档说明;它可能在不同 Python 版本中发生改变(但这种情况极少发生)。
这不是一个通用的“持久化”模块。 marshal
模块主要是为了支持读写 .pyc
文件形式“伪编译”代码的 Python 模块。 因此,Python 维护者保留在必要时以不向下兼容的方式修改 marshal 格式的权利。 如果你要序列化和反序列化 Python 对象,请改用 pickle
模块 — 其执行效率相当,版本独立性有保证,并且 pickle 还支持比 marshal 更多样的对象类型。
警告
marshal
模块对于错误或恶意构建的数据来说是不安全的。 永远不要 unmarshal 来自不受信任的或未经验证的来源的数据。
不是所有 Python 对象类型都受支持;一般来说,此模块只能写入和读取不依赖于特定 Python 调用的对象。 下列类型是受支持的:布尔值、整数、浮点数、复数、字符串、字节串、字节数组、元组、列表、集合、冻结集合、字典和代码对象,需要了解的一点是元组、列表、集合、冻结集合和字典只在其所包含的值也是这些值时才受支持。 单例对象 None
, Ellipsis
and StopIteration
也可以被 marshal 和 unmarshal。 对于 version 低于 3 的格式,递归列表、集合和字典无法被写入(见下文)。
有些函数可以读/写文件,还有些函数可以操作字节类对象。
这个模块定义了以下函数:
marshal.dump
(value, file[, version])
向打开的文件写入值。 值必须为受支持的类型。 文件必须为可写的 binary file。
如果值具有(或所包含的对象具有)不受支持的类型,则会引发 ValueError
—- 但是将向文件写入垃圾数据。 对象也将不能正确地通过 load()
重新读取。
version 参数指明 dump
应当使用的数据格式(见下文)。
引发一个 审计事件 marshal.dumps
,附带参数 value
, version
。
marshal.load
(file)
从打开的文件读取一个值并返回。 如果读不到有效的值(例如由于数据为不同 Python 版本的不兼容 marshal 格式),则会引发 EOFError
, ValueError
或 TypeError
。 文件必须为可读的 binary file。
引发一个 审计事件 marshal.load
,没有附带参数。
注解
如果通过 dump()
marshal 了一个包含不受支持类型的对象,load()
将为不可 marshal 的类型替换 None
。
在 3.10 版更改: 使用此调用为每个代码对象引发一个 code.__new__
审计事件。 现在它会为整个载入操作引发单个 marshal.load
事件。
marshal.dumps
(value[, version])
返回将通过 dump(value, file)
被写入一个文件的字节串对象。 值必须属于受支持的类型。 如果值属于(或包含的对象属于)不受支持的类型则会引发 ValueError
。
version 参数指明 dumps
应当使用的数据类型(见下文)。
引发一个 审计事件 marshal.dumps
,附带参数 value
, version
。
marshal.loads
(bytes)
将 bytes-like object 转换为一个值。 如果找不到有效的值,则会引发 EOFError
, ValueError
或 TypeError
。 输入的额外字节串会被忽略。
引发一个 审计事件 marshal.loads
,附带参数 bytes
。
在 3.10 版更改: 使用此调用为每个代码对象引发一个 code.__new__
审计事件。 现在它会为整个载入操作引发单个 marshal.loads
事件。
此外,还定义了以下常量:
marshal.version
指明模块所使用的格式。 第 0 版为历史格式,第 1 版为共享固化的字符串,第 2 版对浮点数使用二进制格式。 第 3 版添加了对于对象实例化和递归的支持。 目前使用的为第 4 版。
此模块的名称来源于 Modula-3 (及其他语言) 的设计者所使用的术语,他们使用术语 “marshal” 来表示以自包含的形式传输数据。 严格地说,将数据从内部形式转换为外部形式 (例如用于 RPC 缓冲区) 称为 “marshal” 而其逆过程则称为 “unmarshal”。
dbm
—- Unix “数据库” 接口
源代码: Lib/dbm/init.py
dbm
是一种泛用接口,针对各种 DBM 数据库 —- 包括 dbm.gnu
或 dbm.ndbm
。 如果未安装这些模块中的任何一种,则将使用 dbm.dumb
模块中慢速但简单的实现。 还有一个适用于 Oracle Berkeley DB 的 第三方接口。
exception dbm.error
一个元组,其中包含每个受支持的模块可引发的异常,另外还有一个名为 dbm.error
的特殊异常作为第一项 —- 后者最在引发 dbm.error
时被使用。
dbm.whichdb
(filename)
此函数会猜测各种简单数据库模块中的哪一个是可用的 —- dbm.gnu
, dbm.ndbm
还是 dbm.dumb
—- 应该被用来打开给定的文件。
返回下列值中的一个:如果文件由于不可读或不存在而无法打开则返回 None
;如果文件的格式无法猜测则返回空字符串 (''
);或是包含所需模块名称的字符串,例如 'dbm.ndbm'
或 'dbm.gnu'
。
dbm.open
(file, flag=’r’, mode=438)
打开数据库文件 file 并返回一个相应的对象。
如果数据库文件已存在,则使用 whichdb()
函数来确定其类型和要使用的适当模块;如果文件不存在,则会使用上述可导入模块中的第一个。
可选的 flag 参数可以是:
值 | 含意 |
---|---|
‘r’ |
以只读方式打开现有数据库(默认) |
‘w’ |
以读写方式打开现有数据库 |
‘c’ |
以读写方式打开数据库,如果不存在则创建它 |
‘n’ |
始终创建一个新的空数据库,以读写方式打开 |
可选的 mode 参数是文件的 Unix 模式,仅在要创建数据库时才会被使用。 其默认值为八进制数 0o666
(并将被当前的 umask 所修改)。
open()
所返回的对象支持与字典相同的基本功能;可以存储、获取和删除键及其对应的值,并可使用 in
运算符和 keys()
方法,以及 get()
和 setdefault()
。
在 3.2 版更改: 现在 get()
和 setdefault()
在所有数据库模块中均可用。
在 3.8 版更改: 从只读数据库中删除键将引发数据库模块专属的错误而不是 KeyError
。
键和值总是被存储为字节串。 这意味着当使用字符串时它们会在被存储之前隐式地转换至默认编码格式。
这些对象也支持在 with
语句中使用,当语句结束时将自动关闭它们。
在 3.4 版更改: 向 open()
所返回的对象添加了上下文管理协议的原生支持。
以下示例记录了一些主机名和对应的标题,随后将数据库的内容打印出来。:
import dbm
# Open database, creating it if necessary.
with dbm.open('cache', 'c') as db:
# Record some values
db[b'hello'] = b'there'
db['www.python.org'] = 'Python Website'
db['www.cnn.com'] = 'Cable News Network'
# Note that the keys are considered bytes now.
assert db[b'www.python.org'] == b'Python Website'
# Notice how the value is now in bytes.
assert db['www.cnn.com'] == b'Cable News Network'
# Often-used methods of the dict interface work too.
print(db.get('python.org', b'not present'))
# Storing a non-string key or value will raise an exception (most
# likely a TypeError).
db['www.yahoo.com'] = 4
# db is automatically closed when leaving the with statement.
存储非字符串数据的持久化模块。
以下部分描述了各个单独的子模块。
dbm.gnu
—- GNU 对 dbm 的重解析
源代码: Lib/dbm/gnu.py
此模块与 dbm
模块很相似,但是改用 GNU 库 gdbm
来提供某些附加功能。 请注意由 dbm.gnu
与 dbm.ndbm
所创建的文件格式是不兼容的。
dbm.gnu
模块提供了对 GNU DBM 库的接口。 dbm.gnu.gdbm
对象的行为类似于映射(字典),区别在于其键和值总是会在存储之前被转换为字节串。 打印 gdbm
对象不会打印出键和值,并且 items()
和 values()
等方法也不受支持。
exception dbm.gnu.error
针对 dbm.gnu
专属错误例如 I/O 错误引发。 KeyError
的引发则针对一般映射错误例如指定了不正确的键。
dbm.gnu.open
(filename[, flag[, mode]])
打开一个 gdbm
数据库并返回 gdbm
对象。 filename 参数为数据库文件名称。
可选的 flag 参数可以是:
值 | 含意 |
---|---|
‘r’ |
以只读方式打开现有数据库(默认) |
‘w’ |
以读写方式打开现有数据库 |
‘c’ |
以读写方式打开数据库,如果不存在则创建它 |
‘n’ |
始终创建一个新的空数据库,以读写方式打开 |
下列附加字符可被添加至旗标以控制数据库的打开方式:
值 | 含意 |
---|---|
‘f’ |
以快速模式打开数据库。写入数据库将不会同步。 |
‘s’ |
同步模式。这将导致数据库的更改立即写入文件。 |
‘u’ |
不要锁定数据库。 |
不是所有旗标都可用于所有版本的 gdbm
。 模块常量 open_flags
为包含受支持旗标字符的字符串。 如果指定了无效的旗标则会引发 error
。
可选的 mode 参数是文件的 Unix 模式,仅在要创建数据库时才会被使用。 其默认值为八进制数 0o666
。
除了与字典类似的方法,gdbm
对象还有以下方法:
gdbm.firstkey
()使用此方法和
nextkey()
方法可以循环遍历数据库中的每个键。 遍历的顺序是按照gdbm
的内部哈希值,而不会根据键的值排序。 此方法将返回起始键。gdbm.nextkey
(key)在遍历中返回 key 之后的的下一个键。 以下代码将打印数据库
db
中的每个键,而不会在内存中创建一个包含所有键的列表:k = db.firstkey() while k is not None: print(k) k = db.nextkey(k)
gdbm.reorganize
()如果你进行了大量删除操作并且想要缩减
gdbm
文件所使用的空间,此例程将可重新组织数据库。 除非使用此重组功能否则gdbm
对象不会缩减数据库文件大小;在其他情况下,被删除的文件空间将会保留并在添加新的 (键, 值) 对时被重用。gdbm.sync
()当以快速模式打开数据库时,此方法会将任何未写入数据强制写入磁盘。
gdbm.close
()关闭
gdbm
数据库。
dbm.ndbm
—- 基于 ndbm 的接口
源代码: Lib/dbm/ndbm.py
dbm.ndbm
模块提供了对 Unix “(n)dbm” 库的接口。 Dbm 对象的行为类似于映射(字典),区别在于其键和值总是被存储为字节串。 打印 dbm
对象不会打印出键和值,并且 items()
和 values()
等方法也不受支持。
此模块可与 “经典classic” ndbm 接口或 GNU GDBM 兼容接口一同使用。 在 Unix 上,configure 脚本将尝试定位适当的头文件来简化此模块的构建。
exception dbm.ndbm.error
针对 dbm.ndbm
专属错误例如 I/O 错误引发。 KeyError
的引发则针对一般映射错误例如指定了不正确的键。
dbm.ndbm.library
所使用的 ndbm
实现库的名称。
dbm.ndbm.open
(filename[, flag[, mode]])
打开一个 dbm 数据库并返回 ndbm
对象。 filename 参数为数据库文件名称(不带 .dir
或 .pag
扩展名)。
可选的 flag 参数必须是下列值之一:
值 | 含意 |
---|---|
‘r’ |
以只读方式打开现有数据库(默认) |
‘w’ |
以读写方式打开现有数据库 |
‘c’ |
以读写方式打开数据库,如果不存在则创建它 |
‘n’ |
始终创建一个新的空数据库,以读写方式打开 |
可选的 mode 参数是文件的 Unix 模式,仅在要创建数据库时才会被使用。 其默认值为八进制数 0o666
(并将被当前的 umask 所修改)。
除了与字典类似的方法,ndbm
对象还有以下方法:
ndbm.close
()关闭
ndbm
数据库。
dbm.dumb
—- 便携式 DBM 实现
源代码: Lib/dbm/dumb.py
注解
dbm.dumb
模块的目的是在更健壮的模块不可用时作为 dbm
模块的最终回退项。 dbm.dumb
不是为高速运行而编写的,也不像其他数据库模块一样被经常使用。
dbm.dumb
模块提供了一个完全以 Python 编写的持久化字典类接口。 不同于 dbm.gnu
等其他模块,它不需要外部库。 与其他持久化映射一样,它的键和值也总是被存储为字节串。
该模块定义以下内容:
exception dbm.dumb.error
针对 dbm.dumb
专属错误例如 I/O 错误引发。 KeyError
的引发则针对一般映射例如指定了不正确的键。
dbm.dumb.open
(filename[, flag[, mode]])
打开一个 dumbdbm
数据库并返回 dumbdbm 对象。 filename 参数为数据库文件的主名称(不带任何特定扩展名)。 创建一个 dumbdbm 数据库时将创建多个带有 .dat
和 .dir
扩展名的文件。
可选的 flag 参数可以是:
值 | 含意 |
---|---|
‘r’ |
以只读方式打开现有数据库(默认) |
‘w’ |
以读写方式打开现有数据库 |
‘c’ |
以读写方式打开数据库,如果不存在则创建它 |
‘n’ |
始终创建一个新的空数据库,以读写方式打开 |
可选的 mode 参数是文件的 Unix 模式,仅在要创建数据库时才会被使用。 其默认值为八进制数 0o666
(并将被当前的 umask 所修改)。
警告
当载入包含足够巨大/复杂条目的数据库时有可能导致 Python 解释器的崩溃,这是由于 Python AST 编译器有栈深度限制。
在 3.5 版更改: open()
在 flag 值为 'n'
时将总是创建一个新的数据库。
在 3.8 版更改: 附带 'r'
旗标打开的数据库现在将是只读的。 附带 'r'
和 'w'
旗标的打开操作不会再创建数据库。
除了 collections.abc.MutableMapping
类所提供的方法,dumbdbm
对象还提供了以下方法:
dumbdbm.sync
()同步磁盘上的目录和数据文件。 此方法会由
Shelve.sync()
方法来调用。dumbdbm.close
()关闭
dumbdbm
数据库。
sqlite3
—- SQLite 数据库 DB-API 2.0 接口模块
源代码: Lib/sqlite3/
SQLite 是一个C语言库,它可以提供一种轻量级的基于磁盘的数据库,这种数据库不需要独立的服务器进程,也允许需要使用一种非标准的 SQL 查询语言来访问它。一些应用程序可以使用 SQLite 作为内部数据存储。可以用它来创建一个应用程序原型,然后再迁移到更大的数据库,比如 PostgreSQL 或 Oracle。
sqlite3 模块由 Gerhard Häring 编写。 它提供了 PEP 249 所描述的符合 DB-API 2.0 规范的 SQL 接口,并要求 SQLite 3.7.15 或更新的版本。
参见
SQLite的主页;它的文档详细描述了它所支持的 SQL 方言的语法和可用的数据类型。
https://www.w3schools.com/sql/
学习 SQL 语法的教程、参考和例子。
PEP 249 - DB-API 2.0 规范
PEP 由 Marc-André Lemburg 撰写。
模块函数和常量
sqlite3.version
这个模块的版本号,是一个字符串。不是 SQLite 库的版本号。
sqlite3.version_info
这个模块的版本号,是一个由整数组成的元组。不是 SQLite 库的版本号。
sqlite3.sqlite_version
使用中的 SQLite 库的版本号,是一个字符串。
sqlite3.sqlite_version_info
使用中的 SQLite 库的版本号,是一个整数组成的元组。
sqlite3.PARSE_DECLTYPES
这个常量可以作为 connect()
函数的 detect_types 参数。
设置这个参数后,sqlite3
模块将解析它返回的每一列申明的类型。它会申明的类型的第一个单词,比如“integer primary key”,它会解析出“integer”,再比如“number(10)”,它会解析出“number”。然后,它会在转换器字典里查找那个类型注册的转换器函数,并调用它。
sqlite3.PARSE_COLNAMES
这个常量可以作为 connect()
函数的 detect_types 参数。
设置此参数可使得 SQLite 接口解析它所返回的每一列的列名。 它将在其中查找形式为 [mytype] 的字符串,然后将 ‘mytype’ 确定为列的类型。 它将尝试在转换器字典中查找 ‘mytype’ 条目,然后用找到的转换器函数来返回值。 在 Cursor.description
中找到的列名并不包括类型,举例来说,如果你在你的 SQL 中使用了像 'as "Expiration date [datetime]"'
这样的写法,那么我们将解析出在第一个 '['
之前的所有内容并去除前导空格作为列名:即列名将为 “Expiration date”。
sqlite3.connect
(database[, timeout, detect_types, isolation_level, check_same_thread, factory, cached_statements, uri])
连接 SQLite 数据库 database*。默认返回 Connection
对象,除非使用了自定义的 *factory 参数。
database 是准备打开的数据库文件的路径(绝对路径或相对于当前目录的相对路径),它是 path-like object。你也可以用 ":memory:"
在内存中打开一个数据库。
当一个数据库被多个连接访问的时候,如果其中一个进程修改这个数据库,在这个事务提交之前,这个 SQLite 数据库将会被一直锁定。timeout 参数指定了这个连接等待锁释放的超时时间,超时之后会引发一个异常。这个超时时间默认是 5.0(5秒)。
isolation_level 参数,请查看 Connection
对象的 isolation_level
属性。
SQLite 原生只支持5种类型:TEXT,INTEGER,REAL,BLOB 和 NULL。如果你想用其它类型,你必须自己添加相应的支持。使用 detect_types 参数和模块级别的 register_converter()
函数注册转换器 可以简单的实现。
detect_types 默认为 0 (即关闭,不进行类型检测),你可以将其设为任意的 PARSE_DECLTYPES
和 PARSE_COLNAMES
组合来启用类型检测。 由于 SQLite 的行为,生成的字段类型 (例如 max(data)
) 不能被检测,即使在设置了 detect_types 形参时也是如此。 在此情况下,返回的类型为 str
。
默认情况下,check_same_thread 为 True
,只有当前的线程可以使用该连接。 如果设置为 False
,则多个线程可以共享返回的连接。 当多个线程使用同一个连接的时候,用户应该把写操作进行序列化,以避免数据损坏。
默认情况下,当调用 connect 方法的时候,sqlite3
模块使用了它的 Connection
类。当然,你也可以创建 Connection
类的子类,然后创建提供了 factory 参数的 connect()
方法。
sqlite3
模块在内部使用语句缓存来避免 SQL 解析开销。 如果要显式设置当前连接可以缓存的语句数,可以设置 cached_statements 参数。 当前实现的默认值是缓存100条语句。
如果 uri 为真,则 database 被解释为 URI。 它允许您指定选项。 例如,以只读模式打开数据库:
db = sqlite3.connect('file:path/to/database?mode=ro', uri=True)
有关此功能的更多信息,包括已知选项的列表,可以在 SQLite URI 文档 <https://www.sqlite.org/uri.html>
_ 中找到。
引发一个 审计事件 sqlite3.connect
,附带参数 database
。
引发一个 审计事件 sqlite3.connect/handle
,附带参数 connection_handle
。
在 3.4 版更改: 增加了 uri 参数。
在 3.7 版更改: database 现在可以是一个 path-like object 对象了,不仅仅是字符串。
在 3.10 版更改: 增加了 sqlite3.connect/handle
审计事件。
sqlite3.register_converter
(typename, callable)
注册一个回调对象 callable, 用来转换数据库中的字节串为自定的 Python 类型。所有类型为 typename 的数据库的值在转换时,都会调用这个回调对象。通过指定 connect()
函数的 detect-types 参数来设置类型检测的方式。注意,typename 与查询语句中的类型名进行匹配时不区分大小写。
sqlite3.register_adapter
(type, callable)
注册一个回调对象 callable*,用来转换自定义Python类型为一个 SQLite 支持的类型。 这个回调对象 *callable 仅接受一个 Python 值作为参数,而且必须返回以下某个类型的值:int,float,str 或 bytes。
sqlite3.complete_statement
(sql)
如果字符串 sql 包含一个或多个完整的 SQL 语句(以分号结束)则返回 True
。它不会验证 SQL 语法是否正确,仅会验证字符串字面上是否完整,以及是否以分号结束。
它可以用来构建一个 SQLite shell,下面是一个例子:
# A minimal SQLite shell for experiments
import sqlite3
con = sqlite3.connect(":memory:")
con.isolation_level = None
cur = con.cursor()
buffer = ""
print("Enter your SQL commands to execute in sqlite3.")
print("Enter a blank line to exit.")
while True:
line = input()
if line == "":
break
buffer += line
if sqlite3.complete_statement(buffer):
try:
buffer = buffer.strip()
cur.execute(buffer)
if buffer.lstrip().upper().startswith("SELECT"):
print(cur.fetchall())
except sqlite3.Error as e:
print("An error occurred:", e.args[0])
buffer = ""
con.close()
sqlite3.enable_callback_tracebacks
(flag)
默认情况下,您不会获得任何用户定义函数中的回溯消息,比如聚合,转换器,授权器回调等。如果要调试它们,可以设置 flag 参数为 True
并调用此函数。 之后,回调中的回溯信息将会输出到 sys.stderr
。 再次使用 False
来禁用该功能。
连接对象(Connection)
class sqlite3.Connection
SQLite 数据库连接对象有如下的属性和方法:
isolation_level
获取或设置当前默认的隔离级别。 表示自动提交模式的
None
以及 “DEFERRED”, “IMMEDIATE” 或 “EXCLUSIVE” 其中之一。in_transaction
如果是在活动事务中(还没有提交改变),返回
True
,否则,返回False
。它是一个只读属性。3.2 新版功能.
cursor
(factory=Cursor)这个方法接受一个可选参数 factory,如果要指定这个参数,它必须是一个可调用对象,而且必须返回
Cursor
类的一个实例或者子类。commit
()这个方法提交当前事务。如果没有调用这个方法,那么从上一次提交
commit()
以来所有的变化在其他数据库连接上都是不可见的。如果你往数据库里写了数据,但是又查询不到,请检查是否忘记了调用这个方法。rollback
()这个方法回滚从上一次调用
commit()
以来所有数据库的改变。close
()关闭数据库连接。注意,它不会自动调用
commit()
方法。如果在关闭数据库连接之前没有调用commit()
,那么你的修改将会丢失!execute
(sql[, parameters])这是一个非标准的快捷方法,它会调用
cursor()
方法来创建一个游标对象,并使用给定的 parameters 参数来调用游标对象的execute()
方法,最后返回这个游标对象。executemany
(sql[, parameters])这是一个非标准的快捷方法,它会调用
cursor()
方法来创建一个游标对象,并使用给定的 parameters 参数来调用游标对象的executemany()
方法,最后返回这个游标对象。executescript
(sql_script)这是一个非标准的快捷方法,它会调用
cursor()
方法来创建一个游标对象,并使用给定的 sql_script 参数来调用游标对象的executescript()
方法,最后返回这个游标对象。create_function
(name, num_params, func, **, deterministic=False*)创建一个可以在 SQL 语句中使用的用户自定义函数,函数名为 name*。 *num_params 为该函数所接受的形参个数(如果 num_params 为 -1,则该函数可接受任意数量的参数), func 是一个 Python 可调用对象,它将作为 SQL 函数被调用。 如果 deterministic 为真值,则所创建的函数将被标记为 deterministic,这允许 SQLite 执行额外的优化。 此旗标在 SQLite 3.8.3 或更高版本中受到支持,如果在旧版本中使用将引发
NotSupportedError
。此函数可返回任何 SQLite 所支持的类型: bytes, str, int, float 和
None
。在 3.8 版更改: 增加了 deterministic 形参。
示例:
import sqlite3 import hashlib def md5sum(t): return hashlib.md5(t).hexdigest() con = sqlite3.connect(":memory:") con.create_function("md5", 1, md5sum) cur = con.cursor() cur.execute("select md5(?)", (b"foo",)) print(cur.fetchone()[0]) con.close()
create_aggregate
(name, num_params, aggregate_class)创建一个自定义的聚合函数。
参数中 aggregate_class 类必须实现两个方法:
step
和finalize
。step
方法接受 num_params 个参数(如果 num_params 为 -1,那么这个函数可以接受任意数量的参数);finalize
方法返回最终的聚合结果。finalize
方法可以返回任何 SQLite 支持的类型:bytes,str,int,float 和None
。示例:
import sqlite3 class MySum: def __init__(self): self.count = 0 def step(self, value): self.count += value def finalize(self): return self.count con = sqlite3.connect(":memory:") con.create_aggregate("mysum", 1, MySum) cur = con.cursor() cur.execute("create table test(i)") cur.execute("insert into test(i) values (1)") cur.execute("insert into test(i) values (2)") cur.execute("select mysum(i) from test") print(cur.fetchone()[0]) con.close()
create_collation
(name, callable)使用 name 和 callable 创建排序规则。这个 callable 接受两个字符串对象,如果第一个小于第二个则返回 -1, 如果两个相等则返回 0,如果第一个大于第二个则返回 1。注意,这是用来控制排序的(SQL 中的 ORDER BY),所以它不会影响其它的 SQL 操作。
注意,这个 callable 可调用对象会把它的参数作为 Python 字节串,通常会以 UTF-8 编码格式对它进行编码。
以下示例显示了使用“错误方式”进行排序的自定义排序规则:
import sqlite3 def collate_reverse(string1, string2): if string1 == string2: return 0 elif string1 < string2: return 1 else: return -1 con = sqlite3.connect(":memory:") con.create_collation("reverse", collate_reverse) cur = con.cursor() cur.execute("create table test(x)") cur.executemany("insert into test(x) values (?)", [("a",), ("b",)]) cur.execute("select x from test order by x collate reverse") for row in cur: print(row) con.close()
要移除一个排序规则,需要调用
create_collation
并设置 callable 参数为None
。con.create_collation("reverse", None)
interrupt
()可以从不同的线程调用这个方法来终止所有查询操作,这些查询操作可能正在连接上执行。此方法调用之后, 查询将会终止,而且查询的调用者会获得一个异常。
set_authorizer
(authorizer_callback)此方法注册一个授权回调对象。每次在访问数据库中某个表的某一列的时候,这个回调对象将会被调用。如果要允许访问,则返回
SQLITE_OK
,如果要终止整个 SQL 语句,则返回SQLITE_DENY
,如果这一列需要当做 NULL 值处理,则返回SQLITE_IGNORE
。这些常量可以在sqlite3
模块中找到。回调的第一个参数表示要授权的操作类型。 第二个和第三个参数将是参数或
None
,具体取决于第一个参数的值。 第 4 个参数是数据库的名称(“main”,“temp”等),如果需要的话。 第 5 个参数是负责访问尝试的最内层触发器或视图的名称,或者如果此访问尝试直接来自输入 SQL 代码,则为None
。请参阅 SQLite 文档,了解第一个参数的可能值以及第二个和第三个参数的含义,具体取决于第一个参数。
set_progress_handler
(handler, n)此例程注册回调。 对SQLite虚拟机的每个多指令调用回调。 如果要在长时间运行的操作期间从SQLite调用(例如更新用户界面),这非常有用。
如果要清除以前安装的任何进度处理程序,调用该方法时请将 handler 参数设置为
None
。从处理函数返回非零值将终止当前正在执行的查询并导致它引发
OperationalError
异常。set_trace_callback
(trace_callback)为每个 SQLite 后端实际执行的 SQL 语句注册要调用的 trace_callback。
The only argument passed to the callback is the statement (as
str
) that is being executed. The return value of the callback is ignored. Note that the backend does not only run statements passed to theCursor.execute()
methods. Other sources include the transaction management of the sqlite3 module and the execution of triggers defined in the current database.将传入的 trace_callback 设为
None
将禁用跟踪回调。注解
Exceptions raised in the trace callback are not propagated. As a development and debugging aid, use
enable_callback_tracebacks()
to enable printing tracebacks from exceptions raised in the trace callback.3.3 新版功能.
enable_load_extension
(enabled)此例程允许/禁止SQLite引擎从共享库加载SQLite扩展。 SQLite扩展可以定义新功能,聚合或全新的虚拟表实现。 一个众所周知的扩展是与SQLite一起分发的全文搜索扩展。
默认情况下禁用可加载扩展。
引发一个 审计事件
sqlite3.enable_load_extension
,附带参数connection
,enabled
。3.2 新版功能.
在 3.10 版更改: 增加了
sqlite3.enable_load_extension
审计事件。import sqlite3 con = sqlite3.connect(":memory:") # enable extension loading con.enable_load_extension(True) # Load the fulltext search extension con.execute("select load_extension('./fts3.so')") # alternatively you can load the extension using an API call: # con.load_extension("./fts3.so") # disable extension loading again con.enable_load_extension(False) # example from SQLite wiki con.execute("create virtual table recipe using fts3(name, ingredients)") con.executescript(""" insert into recipe (name, ingredients) values ('broccoli stew', 'broccoli peppers cheese tomatoes'); insert into recipe (name, ingredients) values ('pumpkin stew', 'pumpkin onions garlic celery'); insert into recipe (name, ingredients) values ('broccoli pie', 'broccoli cheese onions flour'); insert into recipe (name, ingredients) values ('pumpkin pie', 'pumpkin sugar flour butter'); """) for row in con.execute("select rowid, name, ingredients from recipe where name match 'pie'"): print(row) con.close()
load_extension
(path)此例程从共享库加载SQLite扩展。 在使用此例程之前,必须使用
enable_load_extension()
启用扩展加载。默认情况下禁用可加载扩展。
引发一个 审计事件
sqlite3.load_extension
,附带参数connection
,path
。3.2 新版功能.
在 3.10 版更改: 增加了
sqlite3.load_extension
审计事件。row_factory
您可以将此属性更改为可接受游标和原始行作为元组的可调用对象,并将返回实际结果行。 这样,您可以实现更高级的返回结果的方法,例如返回一个可以按名称访问列的对象。
示例:
import sqlite3 def dict_factory(cursor, row): d = {} for idx, col in enumerate(cursor.description): d[col[0]] = row[idx] return d con = sqlite3.connect(":memory:") con.row_factory = dict_factory cur = con.cursor() cur.execute("select 1 as a") print(cur.fetchone()["a"]) con.close()
如果返回一个元组是不够的,并且你想要对列进行基于名称的访问,你应该考虑将
row_factory
设置为高度优化的sqlite3.Row
类型。Row
提供基于索引和不区分大小写的基于名称的访问,几乎没有内存开销。 它可能比您自己的基于字典的自定义方法甚至基于 db_row 的解决方案更好。text_factory
Using this attribute you can control what objects are returned for the
TEXT
data type. By default, this attribute is set tostr
and thesqlite3
module will returnstr
objects forTEXT
. If you want to returnbytes
instead, you can set it tobytes
.您还可以将其设置为接受单个 bytestring 参数的任何其他可调用对象,并返回结果对象。
请参阅以下示例代码以进行说明:
import sqlite3 con = sqlite3.connect(":memory:") cur = con.cursor() AUSTRIA = "Österreich" # by default, rows are returned as str cur.execute("select ?", (AUSTRIA,)) row = cur.fetchone() assert row[0] == AUSTRIA # but we can make sqlite3 always return bytestrings ... con.text_factory = bytes cur.execute("select ?", (AUSTRIA,)) row = cur.fetchone() assert type(row[0]) is bytes # the bytestrings will be encoded in UTF-8, unless you stored garbage in the # database ... assert row[0] == AUSTRIA.encode("utf-8") # we can also implement a custom text_factory ... # here we implement one that appends "foo" to all strings con.text_factory = lambda x: x.decode("utf-8") + "foo" cur.execute("select ?", ("bar",)) row = cur.fetchone() assert row[0] == "barfoo" con.close()
total_changes
返回自打开数据库连接以来已修改,插入或删除的数据库行的总数。
iterdump
()返回以SQL文本格式转储数据库的迭代器。 保存内存数据库以便以后恢复时很有用。 此函数提供与 sqlite3 shell 中的 .dump 命令相同的功能。
示例:
# Convert file existing_db.db to SQL dump file dump.sql import sqlite3 con = sqlite3.connect('existing_db.db') with open('dump.sql', 'w') as f: for line in con.iterdump(): f.write('%s\n' % line) con.close()
backup
(target, **, pages=- 1, progress=None, name=’main’, sleep=0.25*)即使在 SQLite 数据库被其他客户端访问时,或者同时由同一连接访问,该方法也会对其进行备份。 该副本将写入强制参数 target,该参数必须是另一个
Connection
实例。默认情况下,或者当 pages 为
0
或负整数时,整个数据库将在一个步骤中复制;否则该方法一次循环复制 pages 规定数量的页面。如果指定了 progress,则它必须为
None
或一个将在每次迭代时附带三个整数参数执行的可调用对象,这三个参数分别是前一次迭代的状态 *status,将要拷贝的剩余页数 *remaining 以及总页数 total。name 参数指定将被拷贝的数据库名称:它必须是一个字符串,其内容为表示主数据库的默认值
"main"
,表示临时数据库的"temp"
或是在ATTACH DATABASE
语句的AS
关键字之后指定表示附加数据库的名称。sleep 参数指定在备份剩余页的连续尝试之间要休眠的秒数,可以指定为一个整数或一个浮点数值。
示例一,将现有数据库复制到另一个数据库中:
import sqlite3 def progress(status, remaining, total): print(f'Copied {total-remaining} of {total} pages...') con = sqlite3.connect('existing_db.db') bck = sqlite3.connect('backup.db') with bck: con.backup(bck, pages=1, progress=progress) bck.close() con.close()
示例二,将现有数据库复制到临时副本中:
import sqlite3 source = sqlite3.connect('existing_db.db') dest = sqlite3.connect(':memory:') source.backup(dest)
3.7 新版功能.
Cursor 对象
class sqlite3.Cursor
Cursor
游标实例具有以下属性和方法。
execute
(sql[, parameters])执行一条 SQL 语句。 可以使用 占位符 将值绑定到语句中。
execute()
将只执行一条单独的 SQL 语句。 如果你尝试用它执行超过一条语句,将会引发Warning
。 如果你想要用一次调用执行多条 SQL 语句请使用executescript()
。executemany
(sql, seq_of_parameters)执行一条 带形参的 SQL 命令,使用所有形参序列或在序列 seq_of_parameters 中找到的映射。
sqlite3
模块还允许使用 iterator 代替序列来产生形参。import sqlite3 class IterChars: def __init__(self): self.count = ord('a') def __iter__(self): return self def __next__(self): if self.count > ord('z'): raise StopIteration self.count += 1 return (chr(self.count - 1),) # this is a 1-tuple con = sqlite3.connect(":memory:") cur = con.cursor() cur.execute("create table characters(c)") theIter = IterChars() cur.executemany("insert into characters(c) values (?)", theIter) cur.execute("select c from characters") print(cur.fetchall()) con.close()
这是一个使用生成器 generator 的简短示例:
import sqlite3 import string def char_generator(): for c in string.ascii_lowercase: yield (c,) con = sqlite3.connect(":memory:") cur = con.cursor() cur.execute("create table characters(c)") cur.executemany("insert into characters(c) values (?)", char_generator()) cur.execute("select c from characters") print(cur.fetchall()) con.close()
executescript
(sql_script)这是用于一次性执行多条 SQL 语句的非标准便捷方法。 它会首先发出一条
COMMIT
语句,然后执行通过参数获得的 SQL 脚本。 此方法会忽略isolation_level
;任何事件控制都必须被添加到 sql_script。sql_script 可以是一个
str
类的实例。示例:
import sqlite3 con = sqlite3.connect(":memory:") cur = con.cursor() cur.executescript(""" create table person( firstname, lastname, age ); create table book( title, author, published ); insert into book(title, author, published) values ( 'Dirk Gently''s Holistic Detective Agency', 'Douglas Adams', 1987 ); """) con.close()
fetchone
()获取一个查询结果集的下一行,返回一个单独序列,或是在没有更多可用数据时返回
None
。fetchmany
(size=cursor.arraysize)获取下一个多行查询结果集,返回一个列表。 当没有更多可用行时将返回一个空列表。
每次调用获取的行数由 size 形参指定。 如果没有给出该形参,则由 cursor 的 arraysize 决定要获取的行数。 此方法将基于 size 形参值尝试获取指定数量的行。 如果获取不到指定的行数,则可能返回较少的行。
请注意 size 形参会涉及到性能方面的考虑。为了获得优化的性能,通常最好是使用 arraysize 属性。 如果使用 size 形参,则最好在从一个
fetchmany()
调用到下一个调用之间保持相同的值。fetchall
()获取一个查询结果的所有(剩余)行,返回一个列表。 请注意 cursor 的 arraysize 属性会影响此操作的执行效率。 当没有可用行时将返回一个空列表。
close
()立即关闭 cursor(而不是在当
__del__
被调用的时候)。从这一时刻起该 cursor 将不再可用,如果再尝试用该 cursor 执行任何操作将引发
ProgrammingError
异常。rowcount
虽然
sqlite3
模块的Cursor
类实现了此属性,但数据库引擎本身对于确定 “受影响行”/“已选择行” 的支持并不完善。对于
executemany()
语句,修改行数会被汇总至rowcount
。根据 Python DB API 规格描述的要求,
rowcount
属性 “当未在 cursor 上执行executeXX()
或者上一次操作的 rowcount 不是由接口确定时为 -1”。 这包括SELECT
语句,因为我们无法确定一次查询将产生的行计数,而要等获取了所有行时才会知道。lastrowid
这个只读属性会提供最近修改行的 rowid。 它只在你使用
execute()
方法执行INSERT
或REPLACE
语句时会被设置。 对于INSERT
或REPLACE
以外的操作或者当executemany()
被调用时,lastrowid
会被设为None
。如果
INSERT
或REPLACE
语句操作失败则将返回上一次成功操作的 rowid。在 3.6 版更改: 增加了
REPLACE
语句的支持。arraysize
用于控制
fetchmany()
返回行数的可读取/写入属性。 该属性的默认值为 1,表示每次调用将获取单独一行。description
这个只读属性将提供上一次查询的列名称。 为了与 Python DB API 保持兼容,它会为每个列返回一个 7 元组,每个元组的最后六个条目均为
None
。对于没有任何匹配行的
SELECT
语句同样会设置该属性。connection
这个只读属性将提供
Cursor
对象所使用的 SQLite 数据库Connection
。 通过调用con.cursor()
创建的Cursor
对象所包含的connection
属性将指向 con:>>> con = sqlite3.connect(":memory:") >>> cur = con.cursor() >>> cur.connection == con True
行对象
class sqlite3.Row
一个 Row
实例,该实例将作为用于 Connection
对象的高度优化的 row_factory
。 它的大部分行为都会模仿元组的特性。
它支持使用列名称的映射访问以及索引、迭代、文本表示、相等检测和 len()
等操作。
如果两个 Row
对象具有完全相同的列并且其成员均相等,则它们的比较结果为相等。
keys
()此方法会在一次查询之后立即返回一个列名称的列表,它是
Cursor.description
中每个元组的第一个成员。
在 3.5 版更改: 添加了对切片操作的支持。
让我们假设我们如上面的例子所示初始化一个表:
con = sqlite3.connect(":memory:")
cur = con.cursor()
cur.execute('''create table stocks
(date text, trans text, symbol text,
qty real, price real)''')
cur.execute("""insert into stocks
values ('2006-01-05','BUY','RHAT',100,35.14)""")
con.commit()
cur.close()
现在我们将 Row
插入:
>>> con.row_factory = sqlite3.Row
>>> cur = con.cursor()
>>> cur.execute('select * from stocks')
<sqlite3.Cursor object at 0x7f4e7dd8fa80>
>>> r = cur.fetchone()
>>> type(r)
<class 'sqlite3.Row'>
>>> tuple(r)
('2006-01-05', 'BUY', 'RHAT', 100.0, 35.14)
>>> len(r)
5
>>> r[2]
'RHAT'
>>> r.keys()
['date', 'trans', 'symbol', 'qty', 'price']
>>> r['qty']
100.0
>>> for member in r:
... print(member)
...
2006-01-05
BUY
RHAT
100.0
35.14
异常
exception sqlite3.Warning
Exception
的一个子类。
exception sqlite3.Error
此模块中其他异常的基类。 它是 Exception
的一个子类。
exception sqlite3.DatabaseError
针对数据库相关错误引发的异常。
exception sqlite3.IntegrityError
当数据库的关系一致性受到影响时引发的异常。 例如外键检查失败等。 它是 DatabaseError
的子类。
exception sqlite3.ProgrammingError
编程错误引发的异常,例如表未找到或已存在,SQL 语句存在语法错误,指定的形参数量错误等。 它是 DatabaseError
的子类。
exception sqlite3.OperationalError
与数据库操作相关而不一定能受程序员掌控的错误引发的异常,例如发生非预期的连接中断,数据源名称未找到,事务无法被执行等。 它是 DatabaseError
的子类。
exception sqlite3.NotSupportedError
在使用了某个数据库不支持的方法或数据库 API 时引发的异常,例如在一个不支持事务或禁用了事务的连接上调用 rollback()
方法等。 它是 DatabaseError
的子类。
SQLite 与 Python 类型
概述
SQLite 原生支持如下的类型: NULL
,INTEGER
,REAL
,TEXT
,BLOB
。
因此可以将以下Python类型发送到SQLite而不会出现任何问题:
Python 类型 | SQLite 类型 |
---|---|
None |
NULL |
int |
INTEGER |
float |
REAL |
str |
TEXT |
bytes |
BLOB |
这是SQLite类型默认转换为Python类型的方式:
SQLite 类型 | Python 类型 |
---|---|
NULL |
None |
INTEGER |
int |
REAL |
float |
TEXT |
取决于 text_factory , 默认为 str |
BLOB |
bytes |
sqlite3
模块的类型系统可通过两种方式来扩展:你可以通过对象适配将额外的 Python 类型保存在 SQLite 数据库中,你也可以让 sqlite3
模块通过转换器将 SQLite 类型转换为不同的 Python 类型。
使用适配器将额外的 Python 类型保存在 SQLite 数据库中。
如上文所述,SQLite 只包含对有限类型集的原生支持。 要让 SQLite 能使用其他 Python 类型,你必须将它们 适配 至 sqlite3 模块所支持的 SQLite 类型中的一种:NoneType, int, float, str, bytes。
有两种方式能让 sqlite3
模块将某个定制的 Python 类型适配为受支持的类型。
让对象自行适配
如果类是你自己编写的,这将是一个很好的方式。 假设你有这样一个类:
class Point:
def __init__(self, x, y):
self.x, self.y = x, y
现在你可将这种点对象保存在一个 SQLite 列中。 首先你必须选择一种受支持的类型用来表示点对象。 让我们就用 str 并使用一个分号来分隔坐标值。 然后你需要给你的类加一个方法 __conform__(self, protocol)
,它必须返回转换后的值。 形参 protocol 将为 PrepareProtocol
。
import sqlite3
class Point:
def __init__(self, x, y):
self.x, self.y = x, y
def __conform__(self, protocol):
if protocol is sqlite3.PrepareProtocol:
return "%f;%f" % (self.x, self.y)
con = sqlite3.connect(":memory:")
cur = con.cursor()
p = Point(4.0, -3.2)
cur.execute("select ?", (p,))
print(cur.fetchone()[0])
con.close()
注册可调用的适配器
另一种可能的做法是创建一个将该类型转换为字符串表示的函数并使用 register_adapter()
注册该函数。
import sqlite3
class Point:
def __init__(self, x, y):
self.x, self.y = x, y
def adapt_point(point):
return "%f;%f" % (point.x, point.y)
sqlite3.register_adapter(Point, adapt_point)
con = sqlite3.connect(":memory:")
cur = con.cursor()
p = Point(4.0, -3.2)
cur.execute("select ?", (p,))
print(cur.fetchone()[0])
con.close()
sqlite3
模块有两个适配器可用于 Python 的内置 datetime.date
和 datetime.datetime
类型。 现在假设我们想要存储 datetime.datetime
对象,但不是表示为 ISO 格式,而是表示为 Unix 时间戳。
import sqlite3
import datetime
import time
def adapt_datetime(ts):
return time.mktime(ts.timetuple())
sqlite3.register_adapter(datetime.datetime, adapt_datetime)
con = sqlite3.connect(":memory:")
cur = con.cursor()
now = datetime.datetime.now()
cur.execute("select ?", (now,))
print(cur.fetchone()[0])
con.close()
将SQLite 值转换为自定义Python 类型
编写适配器让你可以将定制的 Python 类型发送给 SQLite。 但要令它真正有用,我们需要实现从 Python 到 SQLite 再回到 Python 的双向转换。
输入转换器。
让我们回到 Point
类。 我们以字符串形式在 SQLite 中存储了 x 和 y 坐标值。
首先,我们将定义一个转换器函数,它接受这样的字符串作为形参并根据该参数构造一个 Point
对象。
注解
转换器函数在调用时 总是 会附带一个 bytes
对象,无论你将何种数据类型的值发给 SQLite。
def convert_point(s):
x, y = map(float, s.split(b";"))
return Point(x, y)
现在你需要让 sqlite3
模块知道你从数据库中选取的其实是一个点对象。 有两种方式都可以做到这件事:
- 隐式的声明类型
- 显式的通过列名
下面的示例说明了这两种方法。
import sqlite3
class Point:
def __init__(self, x, y):
self.x, self.y = x, y
def __repr__(self):
return "(%f;%f)" % (self.x, self.y)
def adapt_point(point):
return ("%f;%f" % (point.x, point.y)).encode('ascii')
def convert_point(s):
x, y = list(map(float, s.split(b";")))
return Point(x, y)
# Register the adapter
sqlite3.register_adapter(Point, adapt_point)
# Register the converter
sqlite3.register_converter("point", convert_point)
p = Point(4.0, -3.2)
#########################
# 1) Using declared types
con = sqlite3.connect(":memory:", detect_types=sqlite3.PARSE_DECLTYPES)
cur = con.cursor()
cur.execute("create table test(p point)")
cur.execute("insert into test(p) values (?)", (p,))
cur.execute("select p from test")
print("with declared types:", cur.fetchone()[0])
cur.close()
con.close()
#######################
# 1) Using column names
con = sqlite3.connect(":memory:", detect_types=sqlite3.PARSE_COLNAMES)
cur = con.cursor()
cur.execute("create table test(p)")
cur.execute("insert into test(p) values (?)", (p,))
cur.execute('select p as "p [point]" from test')
print("with column names:", cur.fetchone()[0])
cur.close()
con.close()
默认适配器和转换器
对于 datetime 模块中的 date 和 datetime 类型已提供了默认的适配器。 它们将会以 ISO 日期/ISO 时间戳的形式发给 SQLite。
默认转换器使用的注册名称是针对 datetime.date
的 “date” 和针对 datetime.datetime
的 “timestamp”。
通过这种方式,你可以在大多数情况下使用 Python 的 date/timestamp 对象而无须任何额外处理。 适配器的格式还与实验性的 SQLite date/time 函数兼容。
下面的示例演示了这一点。
import sqlite3
import datetime
con = sqlite3.connect(":memory:", detect_types=sqlite3.PARSE_DECLTYPES|sqlite3.PARSE_COLNAMES)
cur = con.cursor()
cur.execute("create table test(d date, ts timestamp)")
today = datetime.date.today()
now = datetime.datetime.now()
cur.execute("insert into test(d, ts) values (?, ?)", (today, now))
cur.execute("select d, ts from test")
row = cur.fetchone()
print(today, "=>", row[0], type(row[0]))
print(now, "=>", row[1], type(row[1]))
cur.execute('select current_date as "d [date]", current_timestamp as "ts [timestamp]"')
row = cur.fetchone()
print("current_date", row[0], type(row[0]))
print("current_timestamp", row[1], type(row[1]))
con.close()
如果存储在 SQLite 中的时间戳的小数位多于 6 个数字,则时间戳转换器会将该值截断至微秒精度。
控制事务
底层的 sqlite3
库默认会以 autocommit
模式运行,但 Python 的 sqlite3
模块默认则不使用此模式。
autocommit
模式意味着修改数据库的操作会立即生效。 BEGIN
或 SAVEPOINT
语句会禁用 autocommit
模式,而用于结束外层事务的 COMMIT
, ROLLBACK
或 RELEASE
则会恢复 autocommit
模式。
Python 的 sqlite3
模块默认会在数据修改语言 (DML) 类语句 (即 INSERT
/UPDATE
/DELETE
/REPLACE
) 之前隐式地执行一条 BEGIN
语句。
你可以控制 sqlite3
隐式执行的 BEGIN
语句的种类,具体做法是通过将 isolation_level 形参传给 connect()
调用,或者通过指定连接的 isolation_level
属性。 如果你没有指定 isolation_level,将使用基本的 BEGIN
,它等价于指定 DEFERRED
。 其他可能的值为 IMMEDIATE
和 EXCLUSIVE
。
你可以禁用 sqlite3
模块的隐式事务管理,具体做法是将 isolation_level
设为 None
。 这将使得下层的 sqlite3
库采用 autocommit
模式。 随后你可以通过在代码中显式地使用 BEGIN
, ROLLBACK
, SAVEPOINT
和 RELEASE
语句来完全控制事务状态。
请注意 executescript()
会忽略 isolation_level
;任何事务控制必要要显式地添加。
在 3.6 版更改: 以前 sqlite3
会在 DDL 语句之前隐式地提交未完成事务。 现在则不会再这样做。
有效使用 sqlite3
使用快捷方式
使用 Connection
对象的非标准 execute()
, executemany()
和 executescript()
方法,可以更简洁地编写代码,因为不必显式创建(通常是多余的) Cursor
对象。相反, Cursor
对象是隐式创建的,这些快捷方法返回游标对象。这样,只需对 Connection
对象调用一次,就能直接执行 SELECT
语句并遍历对象。
import sqlite3
langs = [
("C++", 1985),
("Objective-C", 1984),
]
con = sqlite3.connect(":memory:")
# Create the table
con.execute("create table lang(name, first_appeared)")
# Fill the table
con.executemany("insert into lang(name, first_appeared) values (?, ?)", langs)
# Print the table contents
for row in con.execute("select name, first_appeared from lang"):
print(row)
print("I just deleted", con.execute("delete from lang").rowcount, "rows")
# close is not a shortcut method and it's not called automatically,
# so the connection object should be closed manually
con.close()
通过名称而不是索引访问索引
sqlite3
模块的一个有用功能是内置的 sqlite3.Row
类,它被设计用作行对象的工厂。
该类的行装饰器可以用索引(如元组)和不区分大小写的名称访问:
import sqlite3
con = sqlite3.connect(":memory:")
con.row_factory = sqlite3.Row
cur = con.cursor()
cur.execute("select 'John' as name, 42 as age")
for row in cur:
assert row[0] == row["name"]
assert row["name"] == row["nAmE"]
assert row[1] == row["age"]
assert row[1] == row["AgE"]
con.close()
使用连接作为上下文管理器
连接对象可以用来作为上下文管理器,它可以自动提交或者回滚事务。如果出现异常,事务会被回滚;否则,事务会被提交。
import sqlite3
con = sqlite3.connect(":memory:")
con.execute("create table lang (id integer primary key, name varchar unique)")
# Successful, con.commit() is called automatically afterwards
with con:
con.execute("insert into lang(name) values (?)", ("Python",))
# con.rollback() is called after the with block finishes with an exception, the
# exception is still raised and must be caught
try:
with con:
con.execute("insert into lang(name) values (?)", ("Python",))
except sqlite3.IntegrityError:
print("couldn't add Python twice")
# Connection object used as context manager only commits or rollbacks transactions,
# so the connection object should be closed manually
con.close()
数据压缩和存档
zlib
—- 与 gzip 兼容的压缩gzip
—- 对 gzip 格式的支持- 用法示例
- 命令行界面
- 命令行选项
bz2
—- 对 bzip2 压缩算法的支持- 文件压缩和解压
- 增量压缩和解压
- 一次性压缩或解压缩
- 用法示例
lzma
—- 用 LZMA 算法压缩- 读写压缩文件
- 在内存中压缩和解压缩数据
- 杂项
- 指定自定义的过滤器链
- 例子
zipfile
—- 使用ZIP存档- ZipFile 对象
- Path 对象
- PyZipFile 对象
- ZipInfo 对象
- 命令行接口
- 命令行选项
- 解压缩的障碍
- 由于文件本身
- 文件系统限制
- 资源限制
- 中断
- 提取的默认行为
tarfile
—- 读写tar归档文件- TarFile 对象
- TarInfo 对象
- 命令行接口
- 命令行选项
- 例子
- 受支持的 tar 格式
- Unicode 问题
zlib
—- 与 gzip 兼容的压缩
对于需要数据压缩的应用,此模块中的函数允许使用 zlib 库进行压缩和解压缩。 zlib 库的项目主页是 https://www.zlib.net。 已知此 Python 模块与 1.1.3 之前版本的 zlib 库存在不兼容;1.1.3 版则存在一个 安全缺陷,因此我们推荐使用 1.1.4 或更新的版本。
zlib 的函数有很多选项,一般需要按特定顺序使用。本文档没有覆盖全部的用法。更多详细信息请于 http://www.zlib.net/manual.html 参阅官方手册。
此模块中可用的异常和函数如下:
exception zlib.error
在压缩或解压缩过程中发生错误时的异常。
zlib.adler32
(data[, value])
计算 data 的 Adler-32 校验值。(Adler-32 校验的可靠性与 CRC32 基本相当,但比计算 CRC32 更高效。) 计算的结果是一个 32 位的整数。参数 value 是校验时的起始值,其默认值为 1。借助参数 value 可为分段的输入计算校验值。此算法没有加密强度,不应用于身份验证和数字签名。此算法的目的仅为验证数据的正确性,不适合作为通用散列算法。
在 3.0 版更改: 返回值永远是无符号数。要在所有的 Python 版本和平台上获得相同的值,请使用 adler32(data) & 0xffffffff
。
zlib.compress
(data, /, level=-1)
压缩 data 中的字节,返回含有已压缩内容的 bytes 对象。参数 level 为整数,可取值为 0
到 9
或 -1
,用于指定压缩等级。1
(Z_BEST_SPEED) 表示最快速度和最低压缩率,9
(Z_BEST_COMPRESSION) 表示最慢速度和最高压缩率。0
(Z_NO_COMPRESSION) 表示不压缩。参数默认值为 -1
(Z_DEFAULT_COMPRESSION)。Z_DEFAULT_COMPRESSION 是速度和压缩率之间的平衡 (一般相当于设压缩等级为 6)。函数发生错误时抛出 error
异常。
在 3.6 版更改: 现在,level 可作为关键字参数。
zlib.compressobj
(level=-1, method=DEFLATED, wbits=MAX_WBITS, memLevel=DEF_MEM_LEVEL, strategy=Z_DEFAULT_STRATEGY[, zdict])
返回一个 压缩对象,用来压缩内存中难以容下的数据流。
参数 level 为压缩等级,是整数,可取值为 0
到 9
或 -1
。1
(Z_BEST_SPEED) 表示最快速度和最低压缩率,9
(Z_BEST_COMPRESSION) 表示最慢速度和最高压缩率。0
(Z_NO_COMPRESSION) 表示不压缩。参数默认值为 -1
(Z_DEFAULT_COMPRESSION)。Z_DEFAULT_COMPRESSION 是速度和压缩率之间的平衡 (一般相当于设压缩等级为 6)。
method 表示压缩算法。现在只支持 DEFLATED
这个算法。
参数 wbits 指定压缩数据时所使用的历史缓冲区的大小 (窗口大小),并指定压缩输出是否包含头部或尾部。参数的默认值是 15
(MAX_WBITS)。参数的值分为几个范围:
- +9 至 +15:窗口大小以二为底的对数。 即这些值对应着 512 至 32768 的窗口大小。 更大的值会提供更好的压缩,同时内存开销也会更大。 压缩输出会包含 zlib 特定格式的头部和尾部。
- −9 至 −15:绝对值为窗口大小以二为底的对数。 压缩输出仅包含压缩数据,没有头部和尾部。
- +25 至 +31 = 16 + (9 至 15):后 4 个比特位为窗口大小以二为底的对数。 压缩输出包含一个基本的 gzip 头部,并以校验和为尾部。
参数 memLevel 指定内部压缩操作时所占用内存大小。参数取 1
到 9
。更大的值占用更多的内存,同时速度也更快输出也更小。
参数 strategy 用于调节压缩算法。可取值为 Z_DEFAULT_STRATEGY
、Z_FILTERED
、Z_HUFFMAN_ONLY
、Z_RLE
(zlib 1.2.0.1) 或 Z_FIXED
(zlib 1.2.2.2)。
参数 zdict 指定预定义的压缩字典。它是一个字节序列 (如 bytes
对象),其中包含用户认为要压缩的数据中可能频繁出现的子序列。频率高的子序列应当放在字典的尾部。
在 3.3 版更改: 添加关键字参数 zdict。
zlib.crc32
(data[, value])
计算 data 的 CRC (循环冗余校验) 值。计算的结果是一个 32 位的整数。参数 value 是校验时的起始值,其默认值为 0。借助参数 value 可为分段的输入计算校验值。此算法没有加密强度,不应用于身份验证和数字签名。此算法的目的仅为验证数据的正确性,不适合作为通用散列算法。
在 3.0 版更改: 返回值永远是无符号数。要在所有的 Python 版本和平台上获得相同的值,请使用 crc32(data) & 0xffffffff
。
zlib.decompress
(data, /, wbits=MAX_WBITS, bufsize=DEF_BUF_SIZE)
解压 data 中的字节,返回含有已解压内容的 bytes 对象。参数 wbits 取决于 data 的格式,具体参见下边的说明。bufsize 为输出缓冲区的起始大小。函数发生错误时抛出 error
异常。
wbits 形参控制历史缓冲区的大小(或称“窗口大小”)以及所期望的头部和尾部格式。 它类似于 compressobj()
的形参,但可接受更大范围的值:
- +8 至 +15:窗口尺寸以二为底的对数。 输入必须包含 zlib 头部和尾部。
- 0:根据 zlib 头部自动确定窗口大小。 只从 zlib 1.2.3.5 版起受支持。
- −8 至 −15:使用 wbits 的绝对值作为窗口大小以二为底的对数。 输入必须为原始数据流,没有头部和尾部。
- +24 至 +31 = 16 + (8 至 15):使用后 4 个比特位作为窗口大小以二为底的对数。 输入必须包括 gzip 头部和尾部。
- +40 至 +47 = 32 + (8 至 15):使用后 4 个比特位作为窗口大小以二为底的对数,并且自动接受 zlib 或 gzip 格式。
当解压缩一个数据流时,窗口大小必须不小于用于压缩数据流的原始窗口大小;使用太小的值可能导致 error
异常。 默认 wbits 值对应于最大的窗口大小并且要求包括 zlib 头部和尾部。
bufsize 是用于存放解压数据的缓冲区初始大小。 如果需要更大空间,缓冲区大小将按需增加,因此你不需要让这个值完全精确;对其进行调整仅会节省一点对 malloc()
的调用次数。
在 3.6 版更改: wbits 和 bufsize 可用作关键字参数。
zlib.decompressobj
(wbits=MAX_WBITS[, zdict])
返回一个解压对象,用来解压无法被一次性放入内存的数据流。
wbits 形参控制历史缓冲区的大小(或称“窗口大小”)以及所期望的头部和尾部格式。 它的含义与 对 decompress() 的描述 相同。
zdict 形参指定指定一个预定义的压缩字典。 如果提供了此形参,它必须与产生将解压数据的压缩器所使用的字典相同。
注解
如果 zdict 是一个可变对象 (例如 bytearray
),则你不可在对 decompressobj()
的调用和对解压器的 decompress()
方法的调用之间修改其内容。
在 3.3 版更改: 增加了 zdict 形参。
压缩对象支持以下方法:
Compress.compress
(data)
压缩 data 并返回 bytes 对象,这个对象含有 data 的部分或全部内容的已压缩数据。所得的对象必须拼接在上一次调用 compress()
方法所得数据的后面。缓冲区中可能留存部分输入以供下一次调用。
Compress.flush
([mode])
压缩所有缓冲区的数据并返回已压缩的数据。参数 mode 可以传入的常量为:Z_NO_FLUSH
、Z_PARTIAL_FLUSH
、Z_SYNC_FLUSH
、Z_FULL_FLUSH
、Z_BLOCK
(zlib 1.2.3.4) 或 Z_FINISH
。默认值为 Z_FINISH
。Z_FINISH
关闭已压缩数据流并不允许再压缩其他数据,Z_FINISH
以外的值皆允许这个对象继续压缩数据。调用 flush()
方法并将 mode 设为 Z_FINISH
后会无法再次调用 compress()
,此时只能删除这个对象。
Compress.copy
()
返回此压缩对象的一个拷贝。它可以用来高效压缩一系列拥有相同前缀的数据。
在 3.8 版更改: 添加了对压缩对象执行 copy.copy()
和 copy.deepcopy()
的支持。
解压缩对象支持以下方法:
Decompress.unused_data
一个 bytes 对象,其中包含压缩数据结束之后的任何字节数据。 也就是说,它将为 b""
直到包含压缩数据的末尾字节可用。 如果整个结果字节串都包含压缩数据,它将为一个空的 bytes 对象 b""
。
Decompress.unconsumed_tail
一个 bytes 对象,其中包含未被上一次 decompress()
调用所消耗的任何数据。 此数据不能被 zlib 机制看到,因此你必须将其送回(可能要附带额外的数据拼接)到后续的 decompress()
方法调用以获得正确的输出。
Decompress.eof
一个布尔值,指明是否已到达压缩数据流的末尾。
这使得区分正确构造的压缩数据流和不完整或被截断的压缩数据流成为可能。
3.3 新版功能.
Decompress.decompress
(data, max_length=0)
解压缩 data 并返回 bytes 对象,其中包含对应于 string 中至少一部分数据的解压缩数据。 此数据应当被拼接到之前任何对 decompress()
方法的调用所产生的输出。 部分输入数据可能会被保留在内部缓冲区以供后续处理。
如果可选的形参 max_length 非零则返回值将不会长于 max_length*。 这可能意味着不是所有已压缩输入都能被处理;并且未被消耗的数据将被保存在 unconsumed_tail
属性中。 如果要继续解压缩则这个字节串必须被传给对 decompress()
的后续调用。 如果 *max_length 为零则整个输入都会被解压缩,并且 unconsumed_tail
将为空。
在 3.6 版更改: max_length 可用作关键字参数。
Decompress.flush
([length])
所有挂起的输入会被处理,并且返回包含剩余未压缩输出的 bytes 对象。 在调用 flush()
之后,decompress()
方法将无法被再次调用;唯一可行的操作是删除该对象。
可选的形参 length 设置输出缓冲区的初始大小。
Decompress.copy
()
返回解压缩对象的一个拷贝。 它可以用来在数据流的中途保存解压缩器的状态以便加快随机查找数据流后续位置的速度。
在 3.8 版更改: 添加了对解压缩对象执行 copy.copy()
和 copy.deepcopy()
的支持。
通过下列常量可获取模块所使用的 zlib 库的版本信息:
zlib.ZLIB_VERSION
构建此模块时所用的 zlib 库的版本字符串。它的值可能与运行时所加载的 zlib 不同。运行时加载的 zlib 库的版本字符串为 ZLIB_RUNTIME_VERSION
。
zlib.ZLIB_RUNTIME_VERSION
解释器所加载的 zlib 库的版本字符串。
3.3 新版功能.
参见
zlib 库项目主页。
http://www.zlib.net/manual.html
zlib 库用户手册。提供了库的许多功能的解释和用法。
gzip
—- 对 gzip 格式的支持
源代码: Lib/gzip.py
此模块提供的简单接口帮助用户压缩和解压缩文件,功能类似于 GNU 应用程序 gzip 和 gunzip。
数据压缩由 zlib
模块提供。
gzip
模块提供 GzipFile
类和 open()
、compress()
、decompress()
几个便利的函数。GzipFile
类可以读写 gzip 格式的文件,还能自动压缩和解压缩数据,这让操作压缩文件如同操作普通的 file object 一样方便。
注意,此模块不支持部分可以被 gzip 和 gunzip 解压的格式,如利用 compress 或 pack 压缩所得的文件。
这个模块定义了以下内容:
gzip.open
(filename, mode=’rb’, compresslevel=9, encoding=None, errors=None, newline=None)
以二进制方式或者文本方式打开一个 gzip 格式的压缩文件,返回一个 file object。
filename 参数可以是一个实际的文件名(一个a str
对象或者 bytes
对象), 或者是一个用来读写的已存在的文件对象。
mode 参数可以是二进制模式: 'r'
, 'rb'
, 'a'
, 'ab'
, 'w'
, 'wb'
, 'x'
or 'xb'
, 或者是文本模式 'rt'
, 'at'
, 'wt'
, or 'xt'
。默认值是 'rb'
。
The compresslevel argument is an integer from 0 to 9, as for the GzipFile
constructor.
对于二进制模式,这个函数等价于 GzipFile
构造器:GzipFile(filename, mode, compresslevel)
。在这个例子中,encoding, errors 和 newline 三个参数一定不要设置。
对于文本模式,将会创建一个 GzipFile
对象,并将它封装到一个 io.TextIOWrapper
实例中, 这个实例默认了指定编码,错误抓获行为和行。
在 3.3 版更改: 支持 filename 为一个文件对象,支持文本模式和 encoding, errors 和 newline 参数。
在 3.4 版更改: 支持 'x'
, 'xb'
和‘xt’
三种模式。
在 3.6 版更改: 接受一个 path-like object。
exception gzip.BadGzipFile
针对无效 gzip 文件引发的异常。 它继承自 OSError
。 针对无效 gzip 文件也可能引发 EOFError
和 zlib.error
。
3.8 新版功能.
class gzip.GzipFile
(filename=None, mode=None, compresslevel=9, fileobj=None, mtime=None)
GzipFile
类的构造器支持 truncate()
的异常,与 file object 的大多数方法非常相似。fileobj 和 filename 至少有一个不为空。
新的实例基于 fileobj*,它可以是一个普通文件,一个 io.BytesIO
对象,或者任何一个与文件相似的对象。当 *filename 是一个文件对象时,它的默认值是 None
。
当 fileobj 为 None
时, filename 参数只用于 gzip 文件头中,这个文件有可能包含未压缩文件的源文件名。如果文件可以被识别,默认 fileobj 的文件名;否则默认为空字符串,在这种情况下文件头将不包含源文件名。
mode 参数可以是 'r'
, 'rb'
, 'a'
, 'ab'
, 'w'
, 'wb'
, 'x'
或 'xb'
中的一个,具体取决于文件将被读取还是被写入。 如果可识别则默认为 fileobj 的模式;否则默认为 'rb'
。 在未来的 Python 发布版中将不再使用 fileobj 的模式。 最好总是指定 mode 为写入模式。
需要注意的是,文件默认使用二进制模式打开。如果要以文本模式打开文件一个压缩文件,请使用 open()
方法(或者使用 io.TextIOWrapper
包装 GzipFile
)。
compresslevel 参数是一个从 0
到 9
的整数,用于控制压缩等级;1
最快但压缩比例最小,9
最慢但压缩比例最大。 0
不压缩。默认为 9
。
mtime 参数是一个可选的数字时间戳用于写入流的最后修改字段,。mtime 只在压缩模式中使用。如果省略或者值为 None
,则使用当前时间。
调用 GzipFile
的 close()
方法不会关闭 fileobj,因为你可以希望增加其它内容到已经压缩的数中。你可以将一个 io.BytesIO
对象作为 fileobj,也可以使用 io.BytesIO
的 getvalue()
方法从内存缓存中恢复数据。
GzipFile
支持 io.BufferedIOBase
类的接口, 包括迭代和 with
语句。只有 truncate()
方法没有实现。
GzipFile
还提供了以下的方法和属性:
peek
(n)在不移动文件指针的情况下读取 n 个未压缩字节。最多只有一个单独的读取流来服务这个方法调用。返回的字节数不一定刚好等于要求的数量。
注解
调用
peek()
并没有改变GzipFile
的文件指针,它可能改变潜在文件对象(例如:GzipFile
使用 fileobj 参数进行初始化)。3.2 新版功能.
mtime
在解压的过程中,最后修改时间字段的值可能来自于这个属性,以整数的形式出现。在读取任何文件头信息前,初始值为
None
。所有 gzip 东方压缩流中必须包含时间戳这个字段。以便于像 gunzip这样的程序可以使用时间戳。格式与
time.time()
的返回值和os.stat()
对象的st_mtime
属性值一样。
在 3.1 版更改: 支持 with
语句,构造器参数 mtime 和 mtime
属性。
在 3.2 版更改: 添加了对零填充和不可搜索文件的支持。
在 3.3 版更改: 实现 io.BufferedIOBase.read1()
方法。
在 3.4 版更改: 支持 'x'
and 'xb'
两种模式。
在 3.5 版更改: 支持写入任意 bytes-like objects。read()
方法可以接受None
为参数。
在 3.6 版更改: 接受一个 path-like object。
3.9 版后已移除: 打开 GzipFile
用于写入而不指定 mode 参数的做法已被弃用。
gzip.compress
(data, compresslevel=9, **, mtime=None*)
压缩 data*,返回一个包含压缩数据的 bytes
对象。 *compresslevel 和 mtime 的含义与上文中 GzipFile
构造器的相同。
3.2 新版功能.
在 3.8 版更改: 添加了 mtime 形参用于可重复的输出。
gzip.decompress
(data)
解压缩 data,返回一个包含未压缩数据的 bytes
对象。
3.2 新版功能.
用法示例
读取压缩文件示例:
import gzip
with gzip.open('/home/joe/file.txt.gz', 'rb') as f:
file_content = f.read()
创建GZIP 文件示例:
import gzip
content = b"Lots of content here"
with gzip.open('/home/joe/file.txt.gz', 'wb') as f:
f.write(content)
使用 GZIP 压缩已有的文件示例:
import gzip
import shutil
with open('/home/joe/file.txt', 'rb') as f_in:
with gzip.open('/home/joe/file.txt.gz', 'wb') as f_out:
shutil.copyfileobj(f_in, f_out)
使用 GZIP 压缩二进制字符串示例:
import gzip
s_in = b"Lots of content here"
s_out = gzip.compress(s_in)
命令行界面
gzip
模块提供了简单的命令行界面用于压缩和解压缩文件。
在执行后 gzip
模块会保留输入文件。
在 3.8 版更改: 添加一个带有用法说明的新命令行界面命令。 默认情况下,当你要执行 CLI 时,默认压缩等级为 6。
命令行选项
file
如果 file 未指定,则从 sys.stdin
读取。
--fast
指明最快速的压缩方法(较低压缩率)。
--best
指明最慢速的压缩方法(最高压缩率)。
-d``,` `--decompress
解压缩给定的文件。
-h``,` `--help
显示帮助消息。
bz2
—- 对 bzip2 压缩算法的支持
源代码: Lib/bz2.py
此模块提供了使用 bzip2 压缩算法压缩和解压数据的一套完整的接口。
bz2
模块包含:
- 用于读写压缩文件的
open()
函数和BZ2File
类。 - 用于增量压缩和解压的
BZ2Compressor
和BZ2Decompressor
类。 - 用于一次性压缩和解压的
compress()
和decompress()
函数。
文件压缩和解压
bz2.open
(filename, mode=’rb’, compresslevel=9, encoding=None, errors=None, newline=None)
以二进制或文本模式打开 bzip2 压缩文件,返回一个 file object。
和 BZ2File
的构造函数类似,filename 参数可以是一个实际的文件名(str
或 bytes
对象),或是已有的可供读取或写入的文件对象。
mode 参数可设为二进制模式的 'r'
、'rb'
、'w'
、'wb'
、'x'
、'xb'
、'a'
或 'ab'
,或者文本模式的 'rt'
、'wt'
、'xt'
或 'at'
。默认是 'rb'
。
compresslevel 参数是 1 到 9 的整数,和 BZ2File
的构造函数一样。
对于二进制模式,这个函数等价于 BZ2File
构造器: BZ2File(filename, mode, compresslevel=compresslevel)
。 在这种情况下,不可提供 encoding, errors 和 newline 参数。
对于文本模式,将会创建一个 BZ2File
对象,并将它包装到一个 io.TextIOWrapper
实例中,此实例带有指定的编码格式、错误处理行为和行结束符。
3.3 新版功能.
在 3.4 版更改: 添加了 'x'
(单独创建) 模式。
在 3.6 版更改: 接受一个 path-like object。
class bz2.BZ2File
(filename, mode=’r’, **, compresslevel=9*)
用二进制模式打开 bzip2 压缩文件。
如果 filename 是一个 str
或 bytes
对象,则打开名称对应的文件目录。 否则的话,filename 应当是一个 file object,它将被用来读取或写入压缩数据。
mode 参数可以是表示读取的 'r'
(默认值),表示覆写的 'w'
,表示单独创建的 'x'
,或表示添加的 'a'
。 这些模式还可分别以 'rb'
, 'wb'
, 'xb'
和 'ab'
的等价形式给出。
如果 filename 是一个文件对象(而不是实际的文件名),则 'w'
模式并不会截断文件,而是会等价于 'a'
。
如果 mode 为 'w'
或 'a'
,则 compresslevel 可以是 1
到 9
之间的整数,用于指定压缩等级: 1
产生最低压缩率,而 9
(默认值) 产生最高压缩率。
如果 mode 为 'r'
,则输入文件可以为多个压缩流的拼接。
BZ2File
提供了 io.BufferedIOBase
所指定的所有成员,但 detach()
和 truncate()
除外。 并支持迭代和 with
语句。
BZ2File
还提供了以下方法:
peek
([n])返回缓冲的数据而不前移文件位置。 至少将返回一个字节的数据(除非为 EOF)。 实际返回的字节数不确定。
注解
虽然调用
peek()
不会改变BZ2File
的文件位置,但它可能改变下层文件对象的位置(举例来说如果BZ2File
是通过传入一个文件对象作为 filename 的话)。3.3 新版功能.
在 3.1 版更改: 添加了对 with
语句的支持。
在 3.3 版更改: 添加了 fileno()
, readable()
, seekable()
, writable()
, read1()
和 readinto()
方法。
在 3.3 版更改: 添加了对 filename 使用 file object 而非实际文件名的支持。
在 3.3 版更改: 添加了 'a'
(append) 模式,以及对读取多数据流文件的支持。
在 3.4 版更改: 添加了 'x'
(单独创建) 模式。
在 3.5 版更改: read()
方法现在接受 None
作为参数。
在 3.6 版更改: 接受一个 path-like object。
在 3.9 版更改: buffering 形参已被移除。 它自 Python 3.0 起即被忽略并弃用。 请传入一个打开文件对象来控制文件的打开方式。
compresslevel 形参成为仅限关键字参数。
在 3.10 版更改: 这个类在面对多个同时读取器和写入器时是线程安全的,就如它在 gzip
和 lzma
中的等价类所具有的特性一样。
增量压缩和解压
class bz2.BZ2Compressor
(compresslevel=9)
创建一个新的压缩器对象。 此对象可被用来执行增量数据压缩。 对于一次性压缩,请改用 compress()
函数。
如果给定 compresslevel,它必须为 1
至 9
之间的整数。 默认值为 9
。
compress
(data)向压缩器对象提供数据。 在可能的情况下返回一段已压缩数据,否则返回空字节串。
当你已结束向压缩器提供数据时,请调用
flush()
方法来完成压缩进程。flush
()结束压缩进程,返回内部缓冲中剩余的压缩完成的数据。
调用此方法之后压缩器对象将不可再被使用。
class bz2.BZ2Decompressor
创建一个新的解压缩器对象。 此对象可被用来执行增量数据解压缩。 对于一次性解压缩,请改用 decompress()
函数。
注解
这个类不会透明地处理包含多个已压缩数据流的输入,这不同于 decompress()
和 BZ2File
。 如果你需要通过 BZ2Decompressor
来解压缩多个数据流输入,你必须为每个数据流都使用新的解压缩器。
decompress
(data, max_length=- 1)解压缩 data (一个 bytes-like object),返回字节串形式的解压缩数据。 某些 data 可以在内部被缓冲,以便用于后续的
decompress()
调用。 返回的数据应当与之前任何decompress()
调用的输出进行拼接。如果 max_length 为非负数,将返回至多 max_length 个字节的解压缩数据。 如果达到此限制并且可以产生后续输出,则
needs_input
属性将被设为False
。 在这种情况下,下一次decompress()
调用提供的 data 可以为b''
以获取更多的输出。如果所有输入数据都已被解压缩并返回(或是因为它少于 max_length 个字节,或是因为 max_length 为负数),则
needs_input
属性将被设为True
。在到达数据流末尾之后再尝试解压缩数据会引发 EOFError。 在数据流末尾之后获取的任何数据都会被忽略并存储至
unused_data
属性。在 3.5 版更改: 添加了 max_length 形参。
eof
若达到了数据流的末尾标记则为
True
。3.3 新版功能.
unused_data
在压缩数据流的末尾之后获取的数据。
如果在达到数据流末尾之前访问此属性,其值将为
b''
。needs_input
如果在要求新的未解压缩输入之前
decompress()
方法可以提供更多的解压缩数据则为False
。3.5 新版功能.
一次性压缩或解压缩
bz2.compress
(data, compresslevel=9)
压缩 data,此参数为一个 字节类对象。
如果给定 compresslevel,它必须为 1
至 9
之间的整数。 默认值为 9
。
对于增量压缩,请改用 BZ2Compressor
。
bz2.decompress
(data)
解压缩 data,此参数为一个 字节类对象。
如果 data 是多个压缩数据流的拼接,则解压缩所有数据流。
对于增量解压缩,请改用 BZ2Decompressor
。
在 3.3 版更改: 支持了多数据流的输入。
用法示例
以下是 bz2
模块典型用法的一些示例。
使用 compress()
和 decompress()
来显示往复式的压缩:
>>> import bz2
>>> data = b"""\
... Donec rhoncus quis sapien sit amet molestie. Fusce scelerisque vel augue
... nec ullamcorper. Nam rutrum pretium placerat. Aliquam vel tristique lorem,
... sit amet cursus ante. In interdum laoreet mi, sit amet ultrices purus
... pulvinar a. Nam gravida euismod magna, non varius justo tincidunt feugiat.
... Aliquam pharetra lacus non risus vehicula rutrum. Maecenas aliquam leo
... felis. Pellentesque semper nunc sit amet nibh ullamcorper, ac elementum
... dolor luctus. Curabitur lacinia mi ornare consectetur vestibulum."""
>>> c = bz2.compress(data)
>>> len(data) / len(c) # Data compression ratio
1.513595166163142
>>> d = bz2.decompress(c)
>>> data == d # Check equality to original object after round-trip
True
使用 BZ2Compressor
进行增量压缩:
>>> import bz2
>>> def gen_data(chunks=10, chunksize=1000):
... """Yield incremental blocks of chunksize bytes."""
... for _ in range(chunks):
... yield b"z" * chunksize
...
>>> comp = bz2.BZ2Compressor()
>>> out = b""
>>> for chunk in gen_data():
... # Provide data to the compressor object
... out = out + comp.compress(chunk)
...
>>> # Finish the compression process. Call this once you have
>>> # finished providing data to the compressor.
>>> out = out + comp.flush()
上面的示例使用了十分“非随机”的数据流(即 b”z” 块数据流)。 随机数据的压缩率通常很差,而有序、重复的数据通常会产生很高的压缩率。
用二进制模式写入和读取 bzip2 压缩文件:
>>> import bz2
>>> data = b"""\
... Donec rhoncus quis sapien sit amet molestie. Fusce scelerisque vel augue
... nec ullamcorper. Nam rutrum pretium placerat. Aliquam vel tristique lorem,
... sit amet cursus ante. In interdum laoreet mi, sit amet ultrices purus
... pulvinar a. Nam gravida euismod magna, non varius justo tincidunt feugiat.
... Aliquam pharetra lacus non risus vehicula rutrum. Maecenas aliquam leo
... felis. Pellentesque semper nunc sit amet nibh ullamcorper, ac elementum
... dolor luctus. Curabitur lacinia mi ornare consectetur vestibulum."""
>>> with bz2.open("myfile.bz2", "wb") as f:
... # Write compressed data to file
... unused = f.write(data)
>>> with bz2.open("myfile.bz2", "rb") as f:
... # Decompress data from file
... content = f.read()
>>> content == data # Check equality to original object after round-trip
True
lzma
—- 用 LZMA 算法压缩
3.3 新版功能.
源代码: Lib/lzma.py
此模块提供了可以压缩和解压缩使用 LZMA 压缩算法的数据的类和便携函数。 其中还包含支持 xz 工具所使用的 .xz
和旧式 .lzma
文件格式的文件接口,以及相应的原始压缩数据流。
此模块所提供了接口与 bz2
模块的非常类似。 请注意 LZMAFile
和 bz2.BZ2File
都 不是 线程安全的。,因此如果你需要在多个线程中使用单个 LZMAFile
实例,则需要通过锁来保护它。
exception lzma.LZMAError
当在压缩或解压缩期间或是在初始化压缩器/解压缩器的状态期间发生错误时此异常会被引发。
读写压缩文件
lzma.open
(filename, mode=’rb’, **, format=None, check=- 1, preset=None, filters=None, encoding=None, errors=None, newline=None*)
以二进制或文本模式打开 LZMA 压缩文件,返回一个 file object。
filename 参数可以是一个实际的文件名(以 str
, bytes
或 路径类 对象的形式给出),在此情况下会打开指定名称的文件,或者可以是一个用于读写的现有文件对象。
mode 参数可以是二进制模式的 "r"
, "rb"
, "w"
, "wb"
, "x"
, "xb"
, "a"
或 "ab"
,或者文本模式的 "rt"
, "wt"
, "xt"
或 "at"
。 默认值为 "rb"
。
当打开一个文件用于读取时,format 和 filters 参数具有与 LZMADecompressor
的参数相同的含义。 在此情况下,check 和 preset 参数不应被使用。
当打开一个文件用于写入的,format, check, preset 和 filters 参数具有与 LZMACompressor
的参数相同的含义。
对于二进制模式,这个函数等价于 LZMAFile
构造器: LZMAFile(filename, mode, ...)
。 在这种情况下,不可提供 encoding, errors 和 newline 参数。
对于文本模式,将会创建一个 LZMAFile
对象,并将它包装到一个 io.TextIOWrapper
实例中,此实例带有指定的编码格式、错误处理行为和行结束符。
在 3.4 版更改: 增加了对 "x"
, "xb"
和 "xt"
模式的支持。
在 3.6 版更改: 接受一个 path-like object。
class lzma.LZMAFile
(filename=None, mode=’r’, **, format=None, check=- 1, preset=None, filters=None*)
以二进制模式打开一个 LZMA 压缩文件。
LZMAFile
可以包装在一个已打开的 file object 中,或者是在给定名称的文件上直接操作。 filename 参数指定所包装的文件对象,或是要打开的文件名称(类型为 str
, bytes
或 路径类 对象)。 如果是包装现有的文件对象,被包装的文件在 LZMAFile
被关闭时将不会被关闭。
mode 参数可以是表示读取的 "r"
(默认值),表示覆写的 "w"
,表示单独创建的 "x"
,或表示添加的 "a"
。 这些模式还可以分别以 "rb"
, "wb"
, "xb"
和 "ab"
的等价形式给出。
如果 filename 是一个文件对象(而不是实际的文件名),则 "w"
模式并不会截断文件,而会等价于 "a"
。
当打开一个文件用于读取时,输入文件可以为多个独立压缩流的拼接。 它们会被作为单个逻辑流被透明地解码。
当打开一个文件用于读取时,format 和 filters 参数具有与 LZMADecompressor
的参数相同的含义。 在此情况下,check 和 preset 参数不应被使用。
当打开一个文件用于写入的,format, check, preset 和 filters 参数具有与 LZMACompressor
的参数相同的含义。
LZMAFile
支持 io.BufferedIOBase
所指定的所有成员,但 detach()
和 truncate()
除外。 并支持迭代和 with
语句。
也提供以下方法:
peek
(size=- 1)返回缓冲的数据而不前移文件位置。 至少将返回一个字节的数据,除非已经到达 EOF。 实际返回的字节数不确定(会忽略 size 参数)。
注解
虽然调用
peek()
不会改变LZMAFile
的文件位置,但它可能改变下层文件对象的位置(举例来说如果LZMAFile
是通过传入一个文件对象作为 filename 的话)。
在 3.4 版更改: 增加了对 "x"
和 "xb"
模式的支持。
在 3.5 版更改: read()
方法现在接受 None
作为参数。
在 3.6 版更改: 接受一个 path-like object。
在内存中压缩和解压缩数据
class lzma.LZMACompressor
(format=FORMAT_XZ, check=- 1, preset=None, filters=None)
创建一个压缩器对象,此对象可被用来执行增量压缩。
压缩单个数据块的更便捷方式请参阅 compress()
。
format 参数指定应当使用哪种容器格式。 可能的值有:
FORMAT_XZ
:.xz
容器格式。这是默认格式。
FORMAT_ALONE
: 传统的.lzma
容器格式。这种格式相比
.xz
更为受限 — 它不支持一致性检查或多重过滤器。FORMAT_RAW
: 原始数据流,不使用任何容器格式。这个格式描述器不支持一致性检查,并且要求你必须指定一个自定义的过滤器链(用于压缩和解压缩)。 此外,以这种方式压缩的数据不可使用
FORMAT_AUTO
来解压缩 。
check 参数指定要包含在压缩数据中的一致性检查类型。 这种检查在解压缩时使用,以确保数据没有被破坏。 可能的值是:
CHECK_NONE
: 没有一致性检查。 这是FORMAT_ALONE
和FORMAT_RAW
的默认值(也是唯一可接受的值)。CHECK_CRC32
: 32 位循环冗余检查。CHECK_CRC64
: 64 位循环冗余检查。 这是FORMAT_XZ
的默认值。CHECK_SHA256
: 256 位安全哈希算法。
如果指定的检查不受支持,则会引发 LZMAError
。
压缩设置可被指定为一个预设的压缩等级(通过 preset 参数)或以自定义过滤器链来详细设置(通过 filters 参数)。
preset 参数(如果提供)应当为一个 0
到 9
(包括边界) 之间的整数,可以选择与常数 PRESET_EXTREME
进行 OR 运算。 如果 preset 和 filters 均未给出,则默认行为是使用 PRESET_DEFAULT
(预设等级 6
)。 更高的预设等级会产生更小的输出,但会使得压缩过程更缓慢。
注解
除了更加 CPU 密集,使用更高的预设等级来压缩还需要更多的内存(并产生需要更多内存来解压缩的输出)。 例如使用预设等级 9
时,一个 LZMACompressor
对象的开销可以高达 800 MiB。 出于这样的原因,通常最好是保持使用默认预设等级。
filters 参数(如果提供)应当指定一个过滤器链。
compress
(data)压缩 data (一个
bytes
object),返回包含针对输入的至少一部分已压缩数据的bytes
对象。 一部 data 可能会被放入内部缓冲区,以便用于后续的compress()
和flush()
调用。 返回的数据应当与之前任何compress()
调用的输出进行拼接。flush
()结束压缩进程,返回包含保存在压缩器的内部缓冲区中的任意数据的
bytes
对象。调用此方法之后压缩器将不可再被使用。
class lzma.LZMADecompressor
(format=FORMAT_AUTO, memlimit=None, filters=None)
创建一个压缩器对象,此对象可被用来执行增量解压缩。
format 参数指定应当被使用的容器格式。 默认值为 FORMAT_AUTO
,它可以解压缩 .xz
和 .lzma
文件。 其他可能的值为 FORMAT_XZ
, FORMAT_ALONE
和 FORMAT_RAW
。
memlimit 参数指定解压缩器可以使用的内存上限(字节数)。 当使用此参数时,如果不可能在给定内存上限之内解压缩输入数据则解压缩将失败并引发 LZMAError
。
filters 参数指定用于创建被解压缩数据流的过滤器链。 此参数在 format 为 FORMAT_RAW
时要求提供,但对于其他格式不应使用。
注解
这个类不会透明地处理包含多个已压缩数据流的输入,这不同于 decompress()
和 LZMAFile
。 要通过 LZMADecompressor
来解压缩多个数据流输入,你必须为每个数据流都创建一个新的解压缩器。
decompress
(data, max_length=- 1)解压缩 data (一个 bytes-like object),返回字节串形式的解压缩数据。 某些 data 可以在内部被缓冲,以便用于后续的
decompress()
调用。 返回的数据应当与之前任何decompress()
调用的输出进行拼接。如果 max_length 为非负数,将返回至多 max_length 个字节的解压缩数据。 如果达到此限制并且可以产生后续输出,则
needs_input
属性将被设为False
。 在这种情况下,下一次decompress()
调用提供的 data 可以为b''
以获取更多的输出。如果所有输入数据都已被解压缩并返回(或是因为它少于 max_length 个字节,或是因为 max_length 为负数),则
needs_input
属性将被设为True
。在到达数据流末尾之后再尝试解压缩数据会引发 EOFError。 在数据流末尾之后获取的任何数据都会被忽略并存储至
unused_data
属性。在 3.5 版更改: 添加了 max_length 形参。
check
输入流使用的一致性检查的 ID。 这可能为
CHECK_UNKNOWN
直到已解压了足够的输入数据来确定它所使用的一致性检查。eof
若达到了数据流的末尾标记则为
True
。unused_data
在压缩数据流的末尾之后获取的数据。
在达到数据流末尾之前,这个值将为
b""
。needs_input
如果在要求新的未解压缩输入之前
decompress()
方法可以提供更多的解压缩数据则为False
。3.5 新版功能.
lzma.compress
(data, format=FORMAT_XZ, check=- 1, preset=None, filters=None)
压缩 data (一个 bytes
对象),返回包含压缩数据的 bytes
对象。
参见上文的 LZMACompressor
了解有关 format, check, preset 和 filters 参数的说明。
lzma.decompress
(data, format=FORMAT_AUTO, memlimit=None, filters=None)
解压缩 data (一个 bytes
对象),返回包含解压缩数据的 bytes
对象。
如果 data 是多个单独压缩数据流的拼接,则解压缩所有相应数据流,并返回结果的拼接。
参见上文的 LZMADecompressor
了解有关 format, memlimit 和 filters 参数的说明。
杂项
lzma.is_check_supported
(check)
如果本系统支持给定的一致性检查则返回 True
。
CHECK_NONE
和 CHECK_CRC32
总是受支持。 CHECK_CRC64
和 CHECK_SHA256
或许不可用,如果你正在使用基于受限制特性集编译的 liblzma 版本的话。
指定自定义的过滤器链
过滤器链描述符是由字典组成的序列,其中每个字典包含单个过滤器的 ID 和选项。 每个字典必须包含键 "id"
,并可能包含额外的键用来指定基于过滤器的选项。 有效的过滤器 ID 如下:
- 压缩过滤器:
FILTER_LZMA1
(配合FORMAT_ALONE
使用)FILTER_LZMA2
(配合FORMAT_XZ
和FORMAT_RAW
使用)
- Delta 过滤器:
FILTER_DELTA
- Branch-Call-Jump (BCJ) 过滤器:
FILTER_X86
FILTER_IA64
FILTER_ARM
FILTER_ARMTHUMB
FILTER_POWERPC
FILTER_SPARC
一个过滤器链最多可由 4 个过滤器组成,并且不能为空。 过滤器链中的最后一个过滤器必须为压缩过滤器,其他过滤器必须为 Delta 或 BCJ 过滤器。
压缩过滤器支持下列选项(指定为表示过滤器的字典中的附加条目):
preset
: 压缩预设选项,用于作为未显式指定的选项的默认值的来源。dict_size
: 以字节表示的字典大小。 这应当在 4 KiB 和 1.5 GiB 之间(包含边界)。lc
: 字面值上下文的比特数。lp
: 字面值位置的比特数。 总计值lc + lp
必须不大于 4。pb
: 位置的比特数;必须不大于 4。mode
:MODE_FAST
或MODE_NORMAL
。nice_len
: 对于一个匹配应当被视为“适宜长度”的值。 这应当小于或等于 273。mf
: 要使用的匹配查找器 —MF_HC3
,MF_HC4
,MF_BT2
,MF_BT3
或MF_BT4
。depth
: 匹配查找器使用的最大查找深度。 0 (默认值) 表示基于其他过滤器选项自动选择。
Delta 过滤器保存字节数据之间的差值,在特定环境下可产生更具重复性的输入。 它支持一个 dist
选项,指明要减去的字节之间的差值大小。 默认值为 1,即相邻字节之间的差值。
BCJ 过滤器主要作用于机器码。 它们会转换机器码内的相对分支、调用和跳转以使用绝对寻址,其目标是提升冗余度以供压缩器利用。 这些过滤器支持一个 start_offset
选项,指明应当被映射到输入数据开头的地址。 默认值为 0。
例子
在已压缩的数据中读取:
import lzma
with lzma.open("file.xz") as f:
file_content = f.read()
创建一个压缩文件:
import lzma
data = b"Insert Data Here"
with lzma.open("file.xz", "w") as f:
f.write(data)
在内存中压缩文件:
import lzma
data_in = b"Insert Data Here"
data_out = lzma.compress(data_in)
增量压缩:
import lzma
lzc = lzma.LZMACompressor()
out1 = lzc.compress(b"Some data\n")
out2 = lzc.compress(b"Another piece of data\n")
out3 = lzc.compress(b"Even more data\n")
out4 = lzc.flush()
# Concatenate all the partial results:
result = b"".join([out1, out2, out3, out4])
写入已压缩数据到已打开的文件:
import lzma
with open("file.xz", "wb") as f:
f.write(b"This data will not be compressed\n")
with lzma.open(f, "w") as lzf:
lzf.write(b"This *will* be compressed\n")
f.write(b"Not compressed\n")
使用自定义过滤器链创建一个已压缩文件:
import lzma
my_filters = [
{"id": lzma.FILTER_DELTA, "dist": 5},
{"id": lzma.FILTER_LZMA2, "preset": 7 | lzma.PRESET_EXTREME},
]
with lzma.open("file.xz", "w", filters=my_filters) as f:
f.write(b"blah blah blah")
zipfile
—- 使用ZIP存档
源代码: Lib/zipfile.py
ZIP 文件格式是一个常用的归档与压缩标准。 这个模块提供了创建、读取、写入、添加及列出 ZIP 文件的工具。 任何对此模块的进阶使用都将需要理解此格式,其定义参见 PKZIP 应用程序笔记。
此模块目前不能处理分卷 ZIP 文件。它可以处理使用 ZIP64 扩展(超过 4 GB 的 ZIP 文件)的 ZIP 文件。它支持解密 ZIP 归档中的加密文件,但是目前不能创建一个加密的文件。解密非常慢,因为它是使用原生 Python 而不是 C 实现的。
这个模块定义了以下内容:
exception zipfile.BadZipFile
为损坏的 ZIP 文件抛出的错误。
3.2 新版功能.
exception zipfile.BadZipfile
BadZipFile
的别名,与旧版本 Python 保持兼容性。
3.2 版后已移除.
exception zipfile.LargeZipFile
当 ZIP 文件需要 ZIP64 功能但是未启用时会抛出此错误。
class zipfile.ZipFile
用于读写 ZIP 文件的类。
class zipfile.Path
用于 zip 文件的兼容 pathlib 的包装器。
3.8 新版功能.
class zipfile.PyZipFile
用于创建包含 Python 库的 ZIP 归档的类。
class zipfile.ZipInfo
(filename=’NoName’, date_time=1980, 1, 1, 0, 0, 0)
用于表示档案内一个成员信息的类。 此类的实例会由 ZipFile
对象的 getinfo()
和 infolist()
方法返回。 大多数 zipfile
模块的用户都不必创建它们,只需使用此模块所创建的实例。 filename 应当是档案成员的全名,date_time 应当是包含六个字段的描述最近修改时间的元组。
zipfile.is_zipfile
(filename)
根据文件的 Magic Number,如果 filename 是一个有效的 ZIP 文件则返回 True
,否则返回 False
。 filename 也可能是一个文件或类文件对象。
在 3.1 版更改: 支持文件或类文件对象。
zipfile.ZIP_STORED
未被压缩的归档成员的数字常数。
zipfile.ZIP_DEFLATED
常用的 ZIP 压缩方法的数字常数。需要 zlib
模块。
zipfile.ZIP_BZIP2
BZIP2 压缩方法的数字常数。需要 bz2
模块。
3.3 新版功能.
zipfile.ZIP_LZMA
LZMA 压缩方法的数字常数。需要 lzma
模块。
3.3 新版功能.
注解
ZIP 文件格式规范包括自 2001 年以来对 bzip2 压缩的支持,以及自 2006 年以来对 LZMA 压缩的支持。但是,一些工具(包括较旧的 Python 版本)不支持这些压缩方法,并且可能拒绝完全处理 ZIP 文件,或者无法提取单个文件。
参见
Phil Katz 编写的 ZIP 文件格式文档,此格式和使用的算法的创建者。
有关 Info-ZIP 项目的 ZIP 存档程序和开发库的信息。
ZipFile 对象
class zipfile.ZipFile
(file, mode=’r’, compression=ZIP_STORED, allowZip64=True, compresslevel=None, **, strict_timestamps=True*)
打开一个 ZIP 文件,file 为一个指向文件的路径(字符串),一个类文件对象或者一个 path-like object。
形参 mode 应当为 'r'
来读取一个存在的文件,'w'
来截断并写入新的文件, 'a'
来添加到一个存在的文件,或者 'x'
来仅新建并写入新的文件。如果 mode 为 'x'
并且 file 指向已经存在的文件,则抛出 FileExistsError
。如果 mode 为 'a'
且 file 为已存在的文件,则格外的文件将被加入。如果 file 不指向 ZIP 文件,之后一个新的 ZIP 归档将被追加为此文件。这是为了将 ZIP 归档添加到另一个文件(例如 python.exe
)。如果 mode 为 'a'
并且文件不存在, 则会新建。如果 mode 为 'r'
或 'a'
, 则文件应当可定位。
compression 是在写入归档时要使用的 ZIP 压缩方法,应为 ZIP_STORED
, ZIP_DEFLATED
, ZIP_BZIP2
或 ZIP_LZMA
;不可识别的值将导致引发 NotImplementedError
。 如果指定了 ZIP_DEFLATED
, ZIP_BZIP2
或 ZIP_LZMA
但相应的模块 (zlib
, bz2
或 lzma
) 不可用,则会引发 RuntimeError
。 默认值为 ZIP_STORED
。
如果 allowZip64 为 True
(默认值) 则当 zipfile 大于 4 GiB 时 zipfile 将创建使用 ZIP64 扩展的 ZIP 文件。 如果该参数为 false
则当 ZIP 文件需要 ZIP64 扩展时 zipfile
将引发异常。
compresslevel 形参控制在将文件写入归档时要使用的压缩等级。 当使用 ZIP_STORED
或 ZIP_LZMA
时无压缩效果。 当使用 ZIP_DEFLATED
时接受整数 0
至 9
。 当使用 ZIP_BZIP2
时接受整数 1
至 9
。
strict_timestamps 参数在设为 False
时允许压缩早于 1980-01-01 的文件,代价时会将时间戳设为 1980-01-01。 类似的行为也会对晚于 2107-12-31 的文件发生,时间戳也会被设为该上限值。
如果创建文件时使用 'w'
, 'x'
或 'a'
模式并且未向归档添加任何文件就执行了 closed
,则会将适当的空归档 ZIP 结构写入文件。
ZipFile 也是一个上下文管理器,因此支持 with
语句。 在这个示例中,myzip 将在 with
语句块执行完成之后被关闭 —- 即使是发生了异常:
with ZipFile('spam.zip', 'w') as myzip:
myzip.write('eggs.txt')
3.2 新版功能: 添加了将 ZipFile
用作上下文管理员的功能。
在 3.3 版更改: 添加了对 bzip2
和 lzma
压缩的支持。
在 3.4 版更改: 默认启用 ZIP64 扩展。
在 3.5 版更改: 添加了对不可查找数据流的支持。 并添加了对 'x'
模式的支持。
在 3.6 版更改: 在此之前,对于不可识别的压缩值将引发普通的 RuntimeError
。
在 3.6.2 版更改: file 形参接受一个 path-like object。
在 3.7 版更改: 添加了 compresslevel 形参。
3.8 新版功能: strict_timestamps 仅限关键字参数
ZipFile.close
()
关闭归档文件。 你必须在退出程序之前调用 close()
否则将不会写入关键记录数据。
ZipFile.getinfo
(name)
返回一个 ZipInfo
对象,其中包含有关归档成员 name 的信息。 针对一个目前并不包含于归档中的名称调用 getinfo()
将会引发 KeyError
。
ZipFile.infolist
()
返回一个列表,其中包含每个归档成员的 ZipInfo
对象。 如果是打开一个现有归档则这些对象的排列顺序与它们对应条目在磁盘上的实际 ZIP 文件中的顺序一致。
ZipFile.namelist
()
返回按名称排序的归档成员列表。
ZipFile.open
(name, mode=’r’, pwd=None, **, force_zip64=False*)
以二进制文件类对象的形式访问一个归档成员。 name 可以是归档内某个文件的名称也可以是某个 ZipInfo
对象。 如果包含了 mode 形参,则它必须为 'r'
(默认值) 或 'w'
。 pwd 为用于解密已加密 ZIP 文件的密码。
open()
也是一个上下文管理器,因此支持 with
语句:
with ZipFile('spam.zip') as myzip:
with myzip.open('eggs.txt') as myfile:
print(myfile.read())
如果 mode 为 'r'
则文件类对象 (ZipExtFile
) 将为只读并且提供下列方法: read()
, readline()
, readlines()
, seek()
, tell()
, __iter__()
, __next__()
。 这些对象可独立于 ZipFile 进行操作。
如果 mode='w'
则返回一个可写入的文件句柄,它将支持 write()
方法。 当一个可写入的文件句柄被打开时,尝试读写 ZIP 文件中的其他文件将会引发 ValueError
。
当写入一个文件时,如果文件大小不能预先确定但是可能超过 2 GiB,可传入 force_zip64=True
以确保标头格式能够支持超大文件。 如果文件大小可以预先确定,则在构造 ZipInfo
对象时应设置 file_size
,并将其用作 name 形参。
注解
open()
, read()
和 extract()
方法可接受文件名或 ZipInfo
对象。 当尝试读取一个包含重复名称成员的 ZIP 文件时你将发现此功能很有好处。
在 3.6 版更改: 移除了对 mode='U'
的支持。 请使用 io.TextIOWrapper
以在 universal newlines 模式中读取已压缩的文本文件。
在 3.6 版更改: open()
现在可以被用来配合 mode='w'
选项来将文件写入归档。
在 3.6 版更改: 在已关闭的 ZipFile 上调用 open()
将引发 ValueError
。 在之前的版本中则会引发 RuntimeError
。
ZipFile.extract
(member, path=None, pwd=None)
从归档中提取出一个成员放入当前工作目录;member 必须为成员的完整名称或 ZipInfo
对象。 成员的文件信息会尽可能精确地被提取。 path 指定一个要提取到的不同目录。 member 可以是一个文件名或 ZipInfo
对象。 pwd 是用于解密文件的密码。
返回所创建的经正规化的路径(对应于目录或新文件)。
注解
如果一个成员文件名为绝对路径,则将去掉驱动器/UNC共享点和前导的(反)斜杠,例如: ///foo/bar
在 Unix 上将变为 foo/bar
,而 C:\foo\bar
在 Windows 上将变为 foo\bar
。 并且一个成员文件名中的所有 ".."
都将被移除,例如: ../../foo../../ba..r
将变为 foo../ba..r
。 在 Windows 上非法字符 (:
, <
, >
, |
, "
, ?
, and *
) 会被替换为下划线 (_
)。
在 3.6 版更改: 在已关闭的 ZipFile 上调用 extract()
将引发 ValueError
。 在之前的版本中则将引发 RuntimeError
。
在 3.6.2 版更改: path 形参接受一个 path-like object。
ZipFile.extractall
(path=None, members=None, pwd=None)
从归档中提取出所有成员放入当前工作目录。 path 指定一个要提取到的不同目录。 members 为可选项且必须为 namelist()
所返回列表的一个子集。 pwd 是用于解密文件的密码。
警告
绝不要未经预先检验就从不可靠的源中提取归档文件。 这样有可能在 path 之外创建文件,例如某些成员具有以 "/"
开始的文件名或带有两个点号 ".."
的文件名。 此模块会尝试防止这种情况。
在 3.6 版更改: 在已关闭的 ZipFile 上调用 extractall()
将引发 ValueError
。 在之前的版本中则将引发 RuntimeError
。
在 3.6.2 版更改: path 形参接受一个 path-like object。
ZipFile.printdir
()
将归档的目录表打印到 sys.stdout
。
ZipFile.setpassword
(pwd)
设置 pwd 为用于提取已加密文件的默认密码。
ZipFile.read
(name, pwd=None)
返回归档中文件 name 的字节数据。 name 是归档中文件的名称,或是一个 ZipInfo
对象。 归档必须以读取或追加方式打开。 pwd 为用于已加密文件的密码,并且如果指定该参数则它将覆盖通过 setpassword()
设置的默认密码。 on a ZipFile that uses a compression method 在使用 ZIP_STORED
, ZIP_DEFLATED
, ZIP_BZIP2
或 ZIP_LZMA
以外的压缩方法的 ZipFile 上调用 read()
将引发 NotImplementedError
。 如果相应的压缩模块不可用也会引发错误。
在 3.6 版更改: 在已关闭的 ZipFile 上调用 read()
将引发 ValueError
。 在之前的版本中则会引发 RuntimeError
。
ZipFile.testzip
()
读取归档中的所有文件并检查它们的 CRC 和文件头。 返回第一个已损坏文件的名称,在其他情况下则返回 None
。
在 3.6 版更改: 在已关闭的 ZipFile 上调用 testzip()
将引发 ValueError
。 在之前的版本中则将引发 RuntimeError
。
ZipFile.write
(filename, arcname=None, compress_type=None, compresslevel=None)
将名为 filename 的文件写入归档,给予的归档名为 arcname (默认情况下将与 filename 一致,但是不带驱动器盘符并会移除开头的路径分隔符)。 compress_type 如果给出,它将覆盖作为构造器 compression 形参对于新条目所给出的值。 类似地,compresslevel 如果给出也将覆盖构造器。 归档必须使用 'w'
, 'x'
或 'a'
模式打开。
注解
归档名称应当是基于归档根目录的相对路径,也就是说,它们不应以路径分隔符开头。
注解
如果 arcname
(或 filename
,如果 arcname
未给出) 包含一个空字节,则归档中该文件的名称将在空字节位置被截断。
注解
文件名开头有一个斜杠可能导致存档文件无法在 Windows 系统上的某些 zip 程序中打开。
在 3.6 版更改: 在使用 'r'
模式创建的 ZipFile 或已关闭的 ZipFile 上调用 write()
将引发 ValueError
。 在之前的版本中则会引发 RuntimeError
。
ZipFile.writestr
(zinfo_or_arcname, data, compress_type=None, compresslevel=None)
将一个文件写入归档。 内容为 data*,它可以是一个 str
或 bytes
的实例;如果是 str
,则会先使用 UTF-8 进行编码。 *zinfo_or_arcname 可以是它在归档中将被给予的名称,或者是 ZipInfo
的实例。 如果它是一个实例,则至少必须给定文件名、日期和时间。 如果它是一个名称,则日期和时间会被设为当前日期和时间。 归档必须以 'w'
, 'x'
或 'a'
模式打开。
如果给定了 compress_type*,它将会覆盖作为新条目构造器的 *compression 形参或在 zinfo_or_arcname (如果是一个 ZipInfo
实例) 中所给出的值。 类似地,如果给定了 compresslevel,它将会覆盖构造器。
注解
当传入一个 ZipInfo
实例作为 zinfo_or_arcname 形参时,所使用的压缩方法将为在给定的 ZipInfo
实例的 compress_type 成员中指定的方法。 默认情况下,ZipInfo
构造器将将此成员设为 ZIP_STORED
。
在 3.2 版更改: compress_type 参数。
在 3.6 版更改: 在使用 'r'
模式创建的 ZipFile 或已关闭的 ZipFile 上调用 writestr()
将引发 ValueError
。 在之前的版本中则会引发 RuntimeError
。
以下数据属性也是可用的:
ZipFile.filename
ZIP 文件的名称。
ZipFile.debug
要使用的调试输出等级。 这可以设为从 0
(默认无输出) 到 3
(最多输出) 的值。 调试信息会被写入 sys.stdout
。
ZipFile.comment
关联到 ZIP 文件的 bytes
对象形式的说明。 如果将说明赋给以 'w'
, 'x'
或 'a'
模式创建的 ZipFile
实例,它的长度不应超过 65535 字节。 超过此长度的说明将被截断。
Path 对象
class zipfile.Path
(root, at=’’)
根据 root
zipfile (它可以是一个 ZipFile
实例或适合传给 ZipFile
构造器的 file
) 构造一个 Path 对象。
at
指定此 Path 在 zipfile 中的位置,例如 ‘dir/file.txt’, ‘dir/‘ 或 ‘’。 默认为空字符串,即指定跟目录。
Path 对象会公开 pathlib.Path
对象的下列特性:
Path 对象可以使用 /
运算符或 joinpath
来进行遍历。
Path.name
最终的路径组成部分。
Path.open
(mode=’r’, , pwd, *)
在当前路径上发起调用 ZipFile.open()
。 允许通过支持的模式打开用于读取或写入文本或二进制数据: ‘r’, ‘w’, ‘rb’, ‘wb’。 当以文本模式打开时位置和关键字参数会被传给 io.TextIOWrapper
,在其他情况下则会被忽略。 pwd
是要传给 ZipFile.open()
的 pwd
形参。
在 3.9 版更改: 增加了对以文本和二进制模式打开的支持。 现在默认为文本模式。
Path.iterdir
()
枚举当前目录的子目录。
Path.is_dir
()
如果当前上下文引用了一个目录则返回 True
。
Path.is_file
()
如果当前上下文引用了一个文件则返回 True
。
Path.exists
()
如果当前上下文引用了 zip 文件内的一个文件或目录则返回 True
。
Path.read_text
(, *)
读取当前文件为 unicode 文本。 位置和关键字参数会被传递给 io.TextIOWrapper
(buffer
除外,它将由上下文确定)。
Path.read_bytes
()
读取当前文件为字节串。
Path.joinpath
(\other*)
返回一个新的 Path 对象,其中合并了每个 other 参数。 以下代码是等价的:
>>> Path(...).joinpath('child').joinpath('grandchild')
>>> Path(...).joinpath('child', 'grandchild')
>>> Path(...) / 'child' / 'grandchild'
在 3.10 版更改: 在 3.10 之前,joinpath
未被写入文档并且只接受一个形参。
PyZipFile 对象
PyZipFile
构造器接受与 ZipFile
构造器相同的形参,以及一个额外的形参 optimize。
class zipfile.PyZipFile
(file, mode=’r’, compression=ZIP_STORED, allowZip64=True, optimize=- 1)
3.2 新版功能: optimize 形参。
在 3.4 版更改: 默认启用 ZIP64 扩展。
实例在 ZipFile
对象所具有的方法以外还附加了一个方法:
writepy
(pathname, basename=’’, filterfunc=None)查找
*.py
文件并将相应的文件添加到归档。如果
PyZipFile
的 optimize 形参未给定或为-1
,则相应的文件为*.pyc
文件,并在必要时进行编译。如果
PyZipFile
的 optimize 形参为0
,1
或2
,则限具有相应优化级别的文件会被添加到归档,并在必要时进行编译。如果 pathname 是文件,则文件名必须以
.py
为后缀,并且只有 (相应的*.pyc
) 文件会被添加到最高层级(不带路径信息)。 如果 pathname 不是以.py
为后缀的文件,则将引发RuntimeError
。 如果它是目录,并且该目录不是一个包目录,则所有的*.pyc
文件会被添加到最高层级。 如果目录是一个包目录,则所有的*.pyc
会被添加到包名所表示的文件路径下,并且如果有任何子目录为包目录,则会以排好的顺序递归地添加这些目录。basename 仅限在内部使用。
如果给定 filterfunc*,则它必须是一个接受单个字符串参数的函数。 在将其添加到归档之前它将被传入每个路径(包括每个单独的完整路径)。 如果 *filterfunc 返回假值,则路径将不会被添加,而如果它是一个目录则其内容将被忽略。 例如,如果我们的测试文件全都位于
test
目录或以字符串test_
打头,则我们可以使用一个 filterfunc 来排除它们:>>> zf = PyZipFile('myprog.zip') >>> def notests(s): ... fn = os.path.basename(s) ... return (not (fn == 'test' or fn.startswith('test_'))) >>> zf.writepy('myprog', filterfunc=notests)
writepy()
方法会产生带有这样一些文件名的归档:string.pyc # Top level name test/__init__.pyc # Package directory test/testall.pyc # Module test.testall test/bogus/__init__.pyc # Subpackage directory test/bogus/myfile.pyc # Submodule test.bogus.myfile
3.4 新版功能: filterfunc 形参。
在 3.6.2 版更改: pathname 形参接受一个 path-like object。
在 3.7 版更改: 递归排序目录条目。
ZipInfo 对象
ZipInfo
类的实例会通过 getinfo()
和 ZipFile
对象的 infolist()
方法返回。 每个对象将存储关于 ZIP 归档的一个成员的信息。
有一个类方法可以为文件系统文件创建 ZipInfo
实例:
classmethod ZipInfo.from_file
(filename, arcname=None, **, strict_timestamps=True*)
为文件系统中的文件构造一个 ZipInfo
实例,并准备将其添加到一个 zip 文件。
filename 应为文件系统中某个文件或目录的路径。
如果指定了 arcname,它会被用作归档中的名称。 如果未指定 *arcname,则所用名称与 *filename 相同,但将去除任何驱动器盘符和打头的路径分隔符。
strict_timestamps 参数在设为 False
时允许压缩早于 1980-01-01 的文件,代价时会将时间戳设为 1980-01-01。 类似的行为也会对晚于 2107-12-31 的文件发生,时间戳也会被设为该上限值。
3.6 新版功能.
在 3.6.2 版更改: filename 形参接受一个 path-like object。
3.8 新版功能: strict_timestamps 仅限关键字参数
实例具有下列方法和属性:
ZipInfo.is_dir
()
如果此归档成员是一个目录则返回 True
。
这会使用条目的名称:目录应当总是以 /
结尾。
3.6 新版功能.
ZipInfo.filename
归档中的文件名称。
ZipInfo.date_time
上次修改存档成员的时间和日期。这是六个值的元组:
索引 | 值 |
---|---|
0 |
Year (>= 1980) |
1 |
月(1为基数) |
2 |
月份中的日期(1为基数) |
3 |
小时(0为基数) |
4 |
分钟(0为基数) |
5 |
秒(0为基数) |
注解
ZIP文件格式不支持1980年以前的时间戳。
ZipInfo.compress_type
归档成员的压缩类型。
ZipInfo.comment
bytes
对象形式的单个归档成员的注释。
ZipInfo.extra
扩展字段数据。 PKZIP Application Note 包含一些保存于该 bytes
对象中的内部结构的注释。
ZipInfo.create_system
创建 ZIP 归档所用的系统。
ZipInfo.create_version
创建 ZIP 归档所用的 PKZIP 版本。
ZipInfo.extract_version
需要用来提取归档的 PKZIP 版本。
ZipInfo.reserved
必须为零。
ZipInfo.flag_bits
ZIP 标志位。
ZipInfo.volume
文件头的分卷号。
ZipInfo.internal_attr
内部属性。
ZipInfo.external_attr
外部文件属性。
ZipInfo.header_offset
文件头的字节偏移量。
ZipInfo.CRC
未压缩文件的 CRC-32。
ZipInfo.compress_size
已压缩数据的大小。
ZipInfo.file_size
未压缩文件的大小。
命令行接口
zipfile
模块提供了简单的命令行接口用于与 ZIP 归档的交互。
如果你想要创建一个新的 ZIP 归档,请在 -c
选项后指定其名称然后列出应当被包含的文件名:
$ python -m zipfile -c monty.zip spam.txt eggs.txt
传入一个字典也是可接受的:
$ python -m zipfile -c monty.zip life-of-brian_1979/
如果你想要将一个 ZIP 归档提取到指定的目录,请使用 -e
选项:
$ python -m zipfile -e monty.zip target-dir/
要获取一个 ZIP 归档中的文件列表,请使用 -l
选项:
$ python -m zipfile -l monty.zip
命令行选项
-l` `<zipfile>
--list` `<zipfile>
列出一个 zipfile 中的文件名。
-c` `<zipfile> <source1> ... <sourceN>
--create` `<zipfile> <source1> ... <sourceN>
基于源文件创建 zipfile。
-e` `<zipfile> <output_dir>
--extract` `<zipfile> <output_dir>
将 zipfile 提取到目标目录中。
-t` `<zipfile>
--test` `<zipfile>
检测 zipfile 是否有效。
解压缩的障碍
zipfile 模块的提取操作可能会由于下面列出的障碍而失败。
由于文件本身
解压缩可能由于不正确的密码 / CRC 校验和 / ZIP 格式或不受支持的压缩 / 解密方法而失败。
文件系统限制
超出特定文件系统上的限制可能会导致解压缩失败。 例如目录条目所允许的字符、文件名的长度、路径名的长度、单个文件的大小以及文件的数量等等。
资源限制
缺乏内存或磁盘空间将会导致解压缩失败。 例如,作用于 zipfile 库的解压缩炸弹 (即 ZIP bomb) 就可能造成磁盘空间耗尽。
中断
在解压缩期间中断执行,例如按下 ctrl-C 或杀死解压缩进程可能会导致归档文件的解压缩不完整。
提取的默认行为
不了解提取的默认行为可能导致不符合期望的解压缩结果。 例如,当提取相同归档两次时,它会不经询问地覆盖文件。
tarfile
—- 读写tar归档文件
源代码: Lib/tarfile.py
tarfile
模块可以用来读写 tar 归档,包括使用 gzip, bz2 和 lzma 压缩的归档。 请使用 zipfile
模块来读写 .zip
文件,或者使用 shutil 的高层级函数。
一些事实和数字:
- 读写
gzip
,bz2
和lzma
解压的归档要求相应的模块可用。 - 支持读取 / 写入 POSIX.1-1988 (ustar) 格式。
- 对 GNU tar 格式的读/写支持,包括 longname 和 longlink 扩展,对所有种类 sparse 扩展的只读支持,包括 sparse 文件的恢复。
- 对 POSIX.1-2001 (pax) 格式的读/写支持。
- 处理目录、正常文件、硬链接、符号链接、fifo 管道、字符设备和块设备,并且能够获取和恢复文件信息例如时间戳、访问权限和所有者等。
在 3.3 版更改: 添加了对 lzma
压缩的支持。
tarfile.open
(name=None, mode=’r’, fileobj=None, bufsize=10240, **kwargs)
针对路径名 name 返回 TarFile
对象。
mode 必须是 'filemode[:compression]'
形式的字符串,其默认值为 'r'
。 以下是模式组合的完整列表:
模式 | action |
---|---|
‘r’ or ‘r:*’ |
打开和读取使用透明压缩(推荐)。 |
‘r:’ |
打开和读取不使用压缩。 |
‘r:gz’ |
打开和读取使用gzip 压缩。 |
‘r:bz2’ |
打开和读取使用bzip2 压缩。 |
‘r:xz’ |
打开和读取使用lzma 压缩。 |
‘x’ 或 ‘x:’ |
创建tarfile不进行压缩。如果文件已经存在,则抛出 FileExistsError 异常。 |
‘x:gz’ |
使用gzip压缩创建tarfile。如果文件已经存在,则抛出 FileExistsError 异常。 |
‘x:bz2’ |
使用bzip2 压缩创建tarfile。如果文件已经存在,则抛出 FileExistsError 异常。 |
‘x:xz’ |
使用lzma 压缩创建tarfile。如果文件已经存在,则抛出 FileExistsError 异常。 |
‘a’ or ‘a:’ |
打开以便在没有压缩的情况下追加。如果文件不存在,则创建该文件。 |
‘w’ or ‘w:’ |
打开用于未压缩的写入。 |
‘w:gz’ |
打开用于 gzip 压缩的写入。 |
‘w:bz2’ |
打开用于 bzip2 压缩的写入。 |
‘w:xz’ |
打开用于 lzma 压缩的写入。 |
请注意 'a:gz'
, 'a:bz2'
或 'a:xz'
是不可能的组合。 如果 mode 不适用于打开特定(压缩的)文件用于读取,则会引发 ReadError
。 请使用 mode 'r'
来避免这种情况。 如果某种压缩方法不受支持,则会引发 CompressionError
。
如果指定了 fileobj*,它会被用作对应于 *name 的以二进制模式打开的 file object 的替代。 它会被设定为处在位置 0。
对于 'w:gz'
, 'r:gz'
, 'w:bz2'
, 'r:bz2'
, 'x:gz'
, 'x:bz2'
等模式,tarfile.open()
接受关键字参数 compresslevel (默认值为 9
) 来指定文件的压缩等级。
对于 'w:xz'
和 'x:xz'
模式,tarfile.open()
接受关键字参数 preset 来指定文件的压缩等级。
针对特殊的目的,还存在第二种 mode 格式: 'filemode|[compression]'
。 tarfile.open()
将返回一个将其数据作为数据块流来处理的 TarFile
对象。 对此文件将不能执行随机查找。 如果给定了 fileobj*,它可以是任何具有 read()
或 write()
方法 (由 *mode 确定) 的对象。 bufsize 指定块大小,默认值为 20 * 512
字节。 可与此格式组合使用的有 sys.stdin
, 套接字 file object 或磁带设备等。 但是,对于这样的 TarFile
对象存在不允许随机访问的限制。 目前可用的模式如下:
模式 | 动作 |
---|---|
`‘r | *’` |
`‘r | ’` |
`‘r | gz’` |
`‘r | bz2’` |
`‘r | xz’` |
`‘w | ’` |
`‘w | gz’` |
`‘w | bz2’` |
`‘w | xz’` |
在 3.5 版更改: 添加了 'x'
(单独创建) 模式。
在 3.6 版更改: name 形参接受一个 path-like object。
class tarfile.TarFile
用于读取和写入 tar 归档的类。 请不要直接使用这个类:而要使用 tarfile.open()
。
tarfile.is_tarfile
(name)
如果 name 是一个 tarfile
能读取的 tar 归档文件则返回 True
。 name 可以为 str
,文件或文件类对象。
在 3.9 版更改: 支持文件或类文件对象。
tarfile
模块定义了以下异常:
exception tarfile.TarError
所有 tarfile
异常的基类。
exception tarfile.ReadError
当一个不能被 tarfile
模块处理或者因某种原因而无效的 tar 归档被打开时将被引发。
exception tarfile.CompressionError
当一个压缩方法不受支持或者当数据无法被正确解码时将被引发。
exception tarfile.StreamError
当达到流式 TarFile
对象的典型限制时将被引发。
exception tarfile.ExtractError
当使用 TarFile.extract()
时针对 non-fatal 所引发的异常,但是仅限 TarFile.errorlevel``== 2
。
exception tarfile.HeaderError
如果获取的缓冲区无效则会由 TarInfo.frombuf()
引发的异常。
以下常量在模块层级上可用:
tarfile.ENCODING
默认的字符编码格式:在 Windows 上为 'utf-8'
,其他系统上则为 sys.getfilesystemencoding()
所返回的值。
以下常量各自定义了一个 tarfile
模块能够创建的 tar 归档格式。
tarfile.USTAR_FORMAT
POSIX.1-1988 (ustar) 格式。
tarfile.GNU_FORMAT
GNU tar 格式。
tarfile.PAX_FORMAT
POSIX.1-2001 (pax) 格式。
tarfile.DEFAULT_FORMAT
用于创建归档的默认格式。 目前为 PAX_FORMAT
。
在 3.8 版更改: 新归档的默认格式已更改为 PAX_FORMAT
而不再是 GNU_FORMAT
。
参见
归档操作
标准 shutil
模块所提供的高层级归档工具的文档。
GNU tar manual, Basic Tar Format
针对 tar 归档文件的文档,包含 GNU tar 扩展。
TarFile 对象
TarFile
对象提供了一个 tar 归档的接口。 一个 tar 归档就是数据块的序列。 一个归档成员(被保存文件)是由一个标头块加多个数据块组成的。 一个文件可以在一个 tar 归档中多次被保存。 每个归档成员都由一个 TarInfo
对象来代表。
TarFile
对象可在 with
语句中作为上下文管理器使用。 当语句块结束时它将自动被关闭。 请注意在发生异常事件时被打开用于写入的归档将不会被终结;只有内部使用的文件对象将被关闭。
3.2 新版功能: 添加了对上下文管理器协议的支持。
class tarfile.TarFile
(name=None, mode=’r’, fileobj=None, format=DEFAULT_FORMAT, tarinfo=TarInfo, dereference=False, ignore_zeros=False, encoding=ENCODING, errors=’surrogateescape’, pax_headers=None, debug=0, errorlevel=0)
下列所有参数都是可选项并且也可作为实例属性来访问。
name 是归档的路径名称。 name 可以是一个 path-like object。 如果给定了 fileobj 则它可以被省略。 在此情况下,如果对象的 name
属性存在则它会被使用。
mode 可以为 'r'
表示从现有归档读取,'a'
表示将数据追加到现有文件,'w'
表示创建新文件覆盖现有文件,或者 'x'
表示仅在文件不存在时创建新文件。
如果给定了 fileobj*,它会被用于读取或写入数据。 如果可以被确定,则 *mode 会被 fileobj 的模式所覆盖。 fileobj 的使用将从位置 0 开始。
注解
当 TarFile
被关闭时,fileobj 不会被关闭。
format 控制用于写入的归档格式。 它必须为在模块层级定义的常量 USTAR_FORMAT
, GNU_FORMAT
或 PAX_FORMAT
中的一个。 当读取时,格式将被自动检测,即使单个归档中存在不同的格式。
tarinfo 参数可以被用来将默认的 TarInfo
类替换为另一个。
如果 dereference 为 False
,则会将符号链接和硬链接添加到归档中。 如果为 True
,则会将目标文件的内容添加到归档中。 在不支持符号链接的系统上参数将不起作用。
如果 ignore_zeros 为 False
,则会将空的数据块当作归档的末尾来处理。 如果为 True
,则会跳过空的(和无效的)数据块并尝试获取尽可能多的成员。 此参数仅适用于读取拼接的或损坏的归档。
debug 可设为从 0
(无调试消息) 到 3
(全部调试消息)。 消息会被写入到 sys.stderr
。
如果 errorlevel 为 0
,则当使用 TarFile.extract()
时会忽略所有错误。 无论何种情况,当启用调试时它们都将被显示为调试输出的错误消息。 如果为 1
,则所有 fatal 错误会被作为 OSError
异常被引发。 如果为 2
,则所有 non-fatal 错误也会被作为 TarError
异常被引发。
encoding 和 errors 参数定义了读取或写入归档所使用的字符编码格式以及要如何处理转换错误。 默认设置将适用于大多数用户。
可选的 pax_headers 参数是字符串的字典,如果 format 为 PAX_FORMAT
它将被作为 pax 全局标头被添加。
在 3.2 版更改: 使用 'surrogateescape'
作为 errors 参数的默认值。
在 3.5 版更改: 添加了 'x'
(单独创建) 模式。
在 3.6 版更改: name 形参接受一个 path-like object。
classmethod TarFile.open
(…)
作为替代的构造器。 tarfile.open()
函数实际上是这个类方法的快捷方式。
TarFile.getmember
(name)
返回成员 name 的 TarInfo
对象。 如果 name 在归档中找不到,则会引发 KeyError
。
注解
如果一个成员在归档中出现超过一次,它的最后一次出现会被视为是最新的版本。
TarFile.getmembers
()
以 TarInfo
对象列表的形式返回归档的成员。 列表的顺序与归档中成员的顺序一致。
TarFile.getnames
()
以名称列表的形式返回成员。 它的顺序与 getmembers()
所返回列表的顺序一致。
TarFile.list
(verbose=True, **, members=None*)
将内容清单打印到 sys.stdout
。 如果 verbose 为 False
,则将只打印成员名称。 如果为 True
,则输出将类似于 ls -l 的输出效果。 如果给定了可选的 members,它必须为 getmembers()
所返回的列表的一个子集。
在 3.5 版更改: 添加了 members 形参。
TarFile.next
()
当 TarFile
被打开用于读取时,以 TarInfo
对象的形式返回归档的下一个成员。 如果不再有可用对象则返回 None
。
TarFile.extractall
(path=’.’, members=None, **, numeric_owner=False*)
将归档中的所有成员提取到当前工作目录或 path 目录。 如果给定了可选的 members,则它必须为 getmembers()
所返回的列表的一个子集。 字典信息例如所有者、修改时间和权限会在所有成员提取完毕后被设置。 这样做是为了避免两个问题:目录的修改时间会在每当在其中创建文件时被重置。 并且如果目录的权限不允许写入,提取文件到目录的操作将失败。
如果 numeric_owner 为 True
,则将使用来自 tarfile 的 uid 和 gid 数值来设置被提取文件的所有者/用户组。 在其他情况下,则会使用来自 tarfile 的名称值。
警告
绝不要未经预先检验就从不可靠的源中提取归档文件。 这样有可能在 path 之外创建文件,例如某些成员具有以 "/"
开始的绝对路径文件名或带有两个点号 ".."
的文件名。
在 3.5 版更改: 添加了 numeric_owner 形参。
在 3.6 版更改: path 形参接受一个 path-like object。
TarFile.extract
(member, path=’’, set_attrs=True, **, numeric_owner=False*)
从归档中提取出一个成员放入当前工作目录,将使用其完整名称。 成员的文件信息会尽可能精确地被提取。 member 可以是一个文件名或 TarInfo
对象。 你可以使用 path 指定一个不同的目录。 path 可以是一个 path-like object。 将会设置文件属性 (owner, mtime, mode) 除非 set_attrs 为假值。
如果 numeric_owner 为 True
,则将使用来自 tarfile 的 uid 和 gid 数值来设置被提取文件的所有者/用户组。 在其他情况下,则会使用来自 tarfile 的名称值。
注解
extract()
方法不会处理某些提取问题。 在大多数情况下你应当考虑使用 extractall()
方法。
警告
查看 extractall()
的警告信息。
在 3.2 版更改: 添加了 set_attrs 形参。
在 3.5 版更改: 添加了 numeric_owner 形参。
在 3.6 版更改: path 形参接受一个 path-like object。
TarFile.extractfile
(member)
将归档中的一个成员提取为文件对象。 member 可以是一个文件名或 TarInfo
对象。 如果 member 是一个常规文件或链接,则会返回一个 io.BufferedReader
对象。 对于所有其他现有成员,则都将返回 None
。 如果 member 未在归档中出现,则会引发 KeyError
。
在 3.3 版更改: 返回一个 io.BufferedReader
对象。
TarFile.add
(name, arcname=None, recursive=True, **, filter=None*)
将文件 name 添加到归档。 name 可以为任意类型的文件(目录、fifo、符号链接等等)。 如果给出 arcname 则它将为归档中的文件指定一个替代名称。 默认情况下会递归地添加目录。 这可以通过将 recursive 设为 False
来避免。 递归操作会按排序顺序添加条目。 如果给定了 filter,它应当为一个接受 TarInfo
对象并返回已修改 TarInfo
对象的函数。 如果它返回 None
则 TarInfo
对象将从归档中被排除。
在 3.2 版更改: 添加了 filter 形参。
在 3.7 版更改: 递归操作按排序顺序添加条目。
TarFile.addfile
(tarinfo, fileobj=None)
将 TarInfo
对象 tarinfo 添加到归档。 如果给定了 fileobj,它应当是一个 binary file,并会从中读取 tarinfo.size
个字节添加到归档。 你可以直接创建 TarInfo
对象,或是使用 gettarinfo()
来创建。
TarFile.gettarinfo
(name=None, arcname=None, fileobj=None)
基于 os.stat()
的结果或者现有文件的相同数据创建一个 TarInfo
。 文件或者是命名为 name,或者是使用文件描述符指定为一个 file object *fileobj。 *name 可以是一个 path-like object。 如果给定了 arcname*,则它将为归档中的文件指定一个替代名称,在其他情况下,名称将从 *fileobj 的 name
属性或 name 参数获取。 名称应当是一个文本字符串。
你可以在使用 addfile()
添加 TarInfo
的某些属性之前修改它们。 如果文件对象不是从文件开头进行定位的普通文件对象,size
之类的属性就可能需要修改。 例如 GzipFile
之类的文件就属于这种情况。 name
也可以被修改,在这种情况下 arcname 可以是一个占位字符串。
在 3.6 版更改: name 形参接受一个 path-like object。
TarFile.close
()
关闭 TarFile
。 在写入模式下,会向归档添加两个表示结束的零数据块。
TarFile.pax_headers
一个包含 pax 全局标头的键值对的字典。
TarInfo 对象
TarInfo
对象代表 TarFile
中的一个文件。 除了会存储所有必要的文件属性(例如文件类型、大小、时间、权限、所有者等),它还提供了一些确定文件类型的有用方法。 此对象 并不 包含文件数据本身。
TarInfo
对象可通过 TarFile
的方法 getmember()
, getmembers()
和 gettarinfo()
返回。
class tarfile.TarInfo
(name=’’)
创建一个 TarInfo
对象。
classmethod TarInfo.frombuf
(buf, encoding, errors)
基于字符串缓冲区 buf 创建并返回一个 TarInfo
对象。
如果缓冲区无效则会引发 HeaderError
。
classmethod TarInfo.fromtarfile
(tarfile)
从 TarFile
对象 tarfile 读取下一个成员并将其作为 TarInfo
对象返回。
TarInfo.tobuf
(format=DEFAULT_FORMAT, encoding=ENCODING, errors=’surrogateescape’)
基于 TarInfo
对象创建一个字符串缓冲区。
在 3.2 版更改: 使用 'surrogateescape'
作为 errors 参数的默认值。
TarInfo
对象具有以下公有数据属性:
TarInfo.name
归档成员的名称。
TarInfo.size
以字节表示的大小。
TarInfo.mtime
上次修改的时间。
TarInfo.mode
权限位。
TarInfo.type
文件类型。 type 通常为以下常量之一: REGTYPE
, AREGTYPE
, LNKTYPE
, SYMTYPE
, DIRTYPE
, FIFOTYPE
, CONTTYPE
, CHRTYPE
, BLKTYPE
, GNUTYPE_SPARSE
。 要更方便地确定一个 TarInfo
对象的类型,请使用下述的 is*()
方法。
TarInfo.linkname
目标文件名的名称,该属性仅在类型为 LNKTYPE
和 SYMTYPE
的 TarInfo
对象中存在。
TarInfo.uid
最初保存该成员的用户的用户 ID。
TarInfo.gid
最初保存该成员的用户的分组 ID。
TarInfo.uname
用户名。
TarInfo.gname
分组名。
TarInfo.pax_headers
一个包含所关联的 pax 扩展标头的键值对的字典。
TarInfo
对象还提供了一些便捷查询方法:
TarInfo.isfile
()
如果 Tarinfo
对象为普通文件则返回 True
。
TarInfo.isreg
()
与 isfile()
相同。
TarInfo.isdir
()
如果为目录则返回 True
。
TarInfo.issym
()
如果为符号链接则返回 True
。
TarInfo.islnk
()
如果为硬链接则返回 True
。
TarInfo.ischr
()
如果为字符设备则返回 True
。
TarInfo.isblk
()
如果为块设备则返回 True
。
TarInfo.isfifo
()
如果为 FIFO 则返回 True
。.
TarInfo.isdev
()
如果为字符设备、块设备或 FIFO 之一则返回 True
。
命令行接口
3.4 新版功能.
tarfile
模块提供了简单的命令行接口以便与 tar 归档进行交互。
如果你想要创建一个新的 tar 归档,请在 -c
选项后指定其名称然后列出应当被包含的文件名:
$ python -m tarfile -c monty.tar spam.txt eggs.txt
传入一个字典也是可接受的:
$ python -m tarfile -c monty.tar life-of-brian_1979/
如果你想要将一个 tar 归档提取到指定的目录,请使用 -e
选项:
$ python -m tarfile -e monty.tar
你也可以通过传入目录名称将一个 tar 归档提取到不同的目录:
$ python -m tarfile -e monty.tar other-dir/
要获取一个 tar 归档中文件的列表,请使用 -l
选项:
$ python -m tarfile -l monty.tar
命令行选项
-l` `<tarfile>
--list` `<tarfile>
列出一个 tarfile 中的文件名。
-c` `<tarfile> <source1> ... <sourceN>
--create` `<tarfile> <source1> ... <sourceN>
基于源文件创建 tarfile。
-e` `<tarfile> [<output_dir>]
--extract` `<tarfile> [<output_dir>]
如果未指定 output_dir 则会将 tarfile 提取到当前目录。
-t` `<tarfile>
--test` `<tarfile>
检测 tarfile 是否有效。
-v``,` `--verbose
更详细地输出结果。
例子
如何将整个 tar 归档提取到当前工作目录:
import tarfile
tar = tarfile.open("sample.tar.gz")
tar.extractall()
tar.close()
如何通过 TarFile.extractall()
使用生成器函数而非列表来提取一个 tar 归档的子集:
import os
import tarfile
def py_files(members):
for tarinfo in members:
if os.path.splitext(tarinfo.name)[1] == ".py":
yield tarinfo
tar = tarfile.open("sample.tar.gz")
tar.extractall(members=py_files(tar))
tar.close()
如何基于一个文件名列表创建未压缩的 tar 归档:
import tarfile
tar = tarfile.open("sample.tar", "w")
for name in ["foo", "bar", "quux"]:
tar.add(name)
tar.close()
使用 with
语句的同一个示例:
import tarfile
with tarfile.open("sample.tar", "w") as tar:
for name in ["foo", "bar", "quux"]:
tar.add(name)
如何读取一个 gzip 压缩的 tar 归档并显示一些成员信息:
import tarfile
tar = tarfile.open("sample.tar.gz", "r:gz")
for tarinfo in tar:
print(tarinfo.name, "is", tarinfo.size, "bytes in size and is ", end="")
if tarinfo.isreg():
print("a regular file.")
elif tarinfo.isdir():
print("a directory.")
else:
print("something else.")
tar.close()
如何创建一个归档并使用 TarFile.add()
中的 filter 形参来重置用户信息:
import tarfile
def reset(tarinfo):
tarinfo.uid = tarinfo.gid = 0
tarinfo.uname = tarinfo.gname = "root"
return tarinfo
tar = tarfile.open("sample.tar.gz", "w:gz")
tar.add("foo", filter=reset)
tar.close()
受支持的 tar 格式
通过 tarfile
模块可以创建三种 tar 格式:
The POSIX.1-1988 ustar 格式 (
USTAR_FORMAT
)。 它支持最多 256 个字符的文件名长度和最多 100 个字符的链接名长度。 文件大小上限为 8 GiB。 这是一种老旧但广受支持的格式。GNU tar 格式 (
GNU_FORMAT
)。 它支持长文件名和链接名、大于 8 GiB 的文件以及稀疏文件。 它是 GNU/Linux 系统上的事实标准。tarfile
完全支持针对长名称的 GNU tar 扩展,稀疏文件支持则限制为只读。POSIX.1-2001 pax 格式 (
PAX_FORMAT
)。 它是几乎无限制的最灵活格式。 它支持长文件名和链接名,大文件以及使用便捷方式存储路径名。 现代的 tar 实现,包括 GNU tar, bsdtar/libarchive 和 star,完全支持扩展 pax 特性;某些老旧或不维护的库可能不受支持,但应当会将 pax 归档视为广受支持的 ustar 格式。 这是目前新建归档的默认格式。它扩展了现有的 ustar 格式,包括用于无法以其他方式存储的附加标头。 存在两种形式的 pax 标头:扩展标头只影响后续的文件标头,全局标头则适用于完整归档并会影响所有后续的文件。 为了便于移植,在 pax 标头中的所有数据均以 UTF-8 编码。
还有一些 tar 格式的其他变种,它们可以被读取但不能被创建:
- 古老的 V7 格式。 这是来自 Unix 第七版的第一个 tar 格式,它只存储常规文件和目录。 名称长度不能超过 100 个字符,并且没有用户/分组名信息。 某些归档在带有非 ASCII 字符字段的情况下会产生计算错误的标头校验和。
- SunOS tar 扩展格式。 此格式是 POSIX.1-2001 pax 格式的一个变种,但并不保持兼容。
Unicode 问题
最初 tar 格式被设计用来在磁带机上生成备份,主要关注于保存文件系统信息。 现在 tar 归档通常用于文件分发和在网络上交换归档。 最初格式(它是所有其他格式的基础)的一个问题是它没有支持不同字符编码格式的概念。 例如,一个在 UTF-8 系统上创建的普通 tar 归档如果包含非 ASCII 字符则将无法在 Latin-1 系统上被正确读取。 文本元数据(例如文件名,链接名,用户/分组名)将变为损坏状态。 不幸的是,没有什么办法能够自动检测一个归档的编码格式。 pax 格式被设计用来解决这个问题。 它使用通用字符编码格式 UTF-8 来存储非 ASCII 元数据。
在 tarfile
中字符转换的细节由 TarFile
类的 encoding 和 errors 关键字参数控制。
encoding 定义了用于归档中元数据的字符编码格式。 默认值为 sys.getfilesystemencoding()
或是回退选项 'ascii'
。 根据归档是被读取还是被写入,元数据必须被解码或编码。 如果没有正确设置 encoding,转换可能会失败。
errors 参数定义了不能被转换的字符将如何处理。 可能的取值在 错误处理方案 小节列出。 默认方案为 'surrogateescape'
,它也被 Python 用于文件系统调用。
对于 PAX_FORMAT
归档(默认格式),encoding 通常是不必要的,因为所有元数据都使用 UTF-8 来存储。 encoding 仅在解码二进制 pax 标头或存储带有替代字符的字符串等少数场景下会被使用。
文件格式
csv
—- CSV 文件读写- 模块内容
- 变种与格式参数
- Reader 对象
- Writer 对象
- 例子
configparser
—- 配置文件解析器- 快速起步
- 支持的数据类型
- 回退值
- 受支持的 INI 文件结构
- 值的插值
- 映射协议访问
- 定制解析器行为
- 旧式 API 示例
- ConfigParser 对象
- RawConfigParser 对象
- 异常
netrc
—- netrc 文件处理- netrc 对象
xdrlib
—- 编码与解码 XDR 数据- Packer 对象
- Unpacker 对象
- 异常
plistlib
—- 生成与解析 Apple.plist
文件- 例子
csv
—- CSV 文件读写
源代码:Lib/csv.py
CSV (Comma Separated Values) 格式是电子表格和数据库中最常见的输入、输出文件格式。在 RFC 4180 规范推出的很多年前,CSV 格式就已经被开始使用了,由于当时并没有合理的标准,不同应用程序读写的数据会存在细微的差别。这种差别让处理多个来源的 CSV 文件变得困难。但尽管分隔符会变化,此类文件的大致格式是相似的,所以编写一个单独的模块以高效处理此类数据,将程序员从读写数据的繁琐细节中解放出来是有可能的。
csv
模块实现了 CSV 格式表单数据的读写。其提供了诸如“以兼容 Excel 的方式输出数据文件”或“读取 Excel 程序输出的数据文件”的功能,程序员无需知道 Excel 所采用 CSV 格式的细节。此模块同样可以用于定义其他应用程序可用的 CSV 格式或定义特定需求的 CSV 格式。
csv
模块中的 reader
类和 writer
类可用于读写序列化的数据。也可使用 DictReader
类和 DictWriter
类以字典的形式读写数据。
模块内容
csv
模块定义了以下函数:
csv.reader
(csvfile, dialect=’excel’, **fmtparams)
返回一个 reader 对象,该对象将逐行遍历 csvfile。csvfile 可以是任何对象,只要这个对象支持 iterator 协议并在每次调用 __next__()
方法时都返回字符串,文件对象 和列表对象均适用。如果 csvfile 是文件对象,则打开它时应使用 newline=''
。可选参数 dialect 是用于不同的 CSV 变种的特定参数组。它可以是 Dialect
类的子类的实例,也可以是 list_dialects()
函数返回的字符串之一。另一个可选关键字参数 fmtparams 可以覆写当前变种格式中的单个格式设置。
csv 文件的每一行都读取为一个由字符串组成的列表。除非指定了 QUOTE_NONNUMERIC
格式选项(在这种情况下,未加引号的字段会转换为浮点数),否则不会执行自动数据类型转换。
一个简短的用法示例:
>>>import csv
>>>with open('eggs.csv', newline='')as csvfile:
... spamreader = csv.reader(csvfile, delimiter=' ', quotechar='|')
...for row in spamreader:
...print(', '.join(row))
Spam,Spam,Spam,Spam,Spam,BakedBeans
Spam,LovelySpam,WonderfulSpam
csv.writer
(csvfile, dialect=’excel’, **fmtparams)
返回一个 writer 对象,该对象负责将用户的数据在给定的文件类对象上转换为带分隔符的字符串。 csvfile 可以是任何具有 write()
方法的对象。 如果 csvfile 是一个文件对象,则打开它时应使用 newline=''
。 可以给出可选的 dialect 形参用来定义一组特定 CSV 变种专属的形参。 它可以是 Dialect
类的某个子类的实例或是 list_dialects()
函数所返回的字符串之一。 还可以给出另一个可选的 fmtparams 关键字参数来覆盖当前变种中的单个格式化形参。 为了尽量简化与实现 DB API 的模块之间的接口,None
值会被当作空字符串写入。 虽然这个转换是不可逆的,但它可以简化 SQL NULL 数据值到 CSV 文件的转储而无需预处理从 cursor.fetch*
调用返回的数据。 在被写入之前所有其他非字符串数据都会先用 str()
来转转为字符串。
一个简短的用法示例:
import csv
with open('eggs.csv','w', newline='')as csvfile:
spamwriter = csv.writer(csvfile, delimiter=' ',
quotechar='|', quoting=csv.QUOTE_MINIMAL)
spamwriter.writerow(['Spam']*5+['Baked Beans'])
spamwriter.writerow(['Spam','Lovely Spam','Wonderful Spam'])
csv.register_dialect
(name[, dialect[, *fmtparams*]])
将 dialect 与 name 关联起来。 name 必须是字符串。 变种的指定可以通过传入一个 Dialect
的子类,或通过 fmtparams 关键字参数,或是两者同时传入,此时关键字参数会覆盖 dialect 形参。
csv.unregister_dialect
(name)
从变种注册表中删除 name 对应的变种。如果 name 不是已注册的变种名称,则抛出 Error
异常。
csv.get_dialect
(name)
返回 name 对应的变种。如果 name 不是已注册的变种名称,则抛出 Error
异常。该函数返回的是不可变的 Dialect
对象。
csv.list_dialects
()
返回所有已注册变种的名称。
csv.field_size_limit
([new_limit])
返回解析器当前允许的最大字段大小。如果指定了 new_limit,则它将成为新的最大字段大小。
csv
模块定义了以下类:
classcsv.DictReader
(f, fieldnames=None, restkey=None, restval=None, dialect=’excel’, \args, *kwds)
创建一个对象,该对象在操作上类似于常规 reader,但是将每行中的信息映射到一个 dict
,该 dict 的键由 fieldnames 可选参数给出。
fieldnames 参数是一个 sequence。如果省略 fieldnames*,则文件 *f 第一行中的值将用作字段名。无论字段名是如何确定的,字典都将保留其原始顺序。
如果某一行中的字段多于字段名,则剩余数据会被放入一个列表,并与 restkey 所指定的字段名 (默认为 None
) 一起保存。 如果某个非空白行的字段少于字段名,则缺失的值会使用 restval 的值来填充 (默认为 None
)。
所有其他可选或关键字参数都传递给底层的 reader
实例。
在 3.6 版更改: 返回的行现在的类型是 OrderedDict
。
在 3.8 版更改: 现在,返回的行是 dict
类型。
一个简短的用法示例:
>>>import csv
>>>with open('names.csv', newline='')as csvfile:
... reader = csv.DictReader(csvfile)
...for row in reader:
...print(row['first_name'], row['last_name'])
...
EricIdle
JohnCleese
>>>print(row)
{'first_name':'John','last_name':'Cleese'}
classcsv.DictWriter
(f, fieldnames, restval=’’, extrasaction=’raise’, dialect=’excel’, \args, *kwds)
创建一个对象,该对象在操作上类似常规 writer,但会将字典映射到输出行。 fieldnames 参数是由键组成的 序列
,它指定字典中值的顺序,这些值会按指定顺序传递给 writerow()
方法并写入文件 f。 如果字典缺少 fieldnames 中的键,则可选参数 restval 用于指定要写入的值。 如果传递给 writerow()
方法的字典的某些键在 fieldnames 中找不到,则可选参数 extrasaction 用于指定要执行的操作。 如果将其设置为默认值 'raise'
,则会引发 ValueError
。 如果将其设置为 'ignore'
,则字典中的其他键值将被忽略。 所有其他可选或关键字参数都传递给底层的 writer
实例。
注意,与 DictReader
类不同,DictWriter
类的 fieldnames 参数不是可选参数。
一个简短的用法示例:
import csv
with open('names.csv','w', newline='')as csvfile:
fieldnames =['first_name','last_name']
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader()
writer.writerow({'first_name':'Baked','last_name':'Beans'})
writer.writerow({'first_name':'Lovely','last_name':'Spam'})
writer.writerow({'first_name':'Wonderful','last_name':'Spam'})
classcsv.Dialect
Dialect
类是一个容器类,其属性包含有如何处理双引号、空白符、分隔符等的信息。 由于缺少严格的 CSV 规格描述,不同的应用程序会产生略有差别的 CSV 数据。 Dialect
实例定义了 reader
和 writer
实例将具有怎样的行为。
所有可用的 Dialect
名称会由 list_dialects()
返回,并且它们可由特定的 reader
和 writer
类通过它们的初始化函数 (__init__
) 来注册,例如:
import csv
with open('students.csv','w', newline='')as csvfile:
writer = csv.writer(csvfile, dialect='unix')
^^^^^^^^^^^^^^
classcsv.excel
excel
类定义了 Excel 生成的 CSV 文件的常规属性。它在变种注册表中的名称是 'excel'
。
classcsv.excel_tab
excel_tab
类定义了 Excel 生成的、制表符分隔的 CSV 文件的常规属性。它在变种注册表中的名称是 'excel-tab'
。
classcsv.unix_dialect
unix_dialect
类定义了在 UNIX 系统上生成的 CSV 文件的常规属性,即使用 '\n'
作为换行符,且所有字段都有引号包围。它在变种注册表中的名称是 'unix'
。
3.2 新版功能.
classcsv.Sniffer
Sniffer
类用于推断 CSV 文件的格式。
Sniffer
类提供了两个方法:
sniff
(sample, delimiters=None)分析给定的 sample 并返回一个
Dialect
子类,该子类中包含了分析出的格式参数。如果给出可选的 delimiters 参数,则该参数会被解释为字符串,该字符串包含了可能的有效定界符。has_header
(sample)分析 sample 文本(假定为 CSV 格式),如果发现其首行为一组列标题则返回
True
。 在检查每一列时,将考虑是否满足两个关键标准之一来估计 sample 是否包含标题:- 第二至第 n 行包含数字值
- 第二至第 n 行包含字符串值,其中至少有一个值的长度与该列预期标题的长度不同。
会对第一行之后的二十行进行采样;如果有超过一半的列 + 行符合标准,则返回
True
。
注解
此方法是一个粗略的启发式方式,有可能产生错误的真值和假值。
使用 Sniffer
的示例:
with open('example.csv', newline='')as csvfile:
dialect = csv.Sniffer().sniff(csvfile.read(1024))
csvfile.seek(0)
reader = csv.reader(csvfile, dialect)
# ... process CSV file contents here ...
csv
模块定义了以下常量:
csv.QUOTE_ALL
指示 writer
对象给所有字段加上引号。
csv.QUOTE_MINIMAL
指示 writer
对象仅为包含特殊字符(例如 定界符、引号字符 或 行结束符 中的任何字符)的字段加上引号。
csv.QUOTE_NONNUMERIC
指示 writer
对象为所有非数字字段加上引号。
指示 reader 将所有未用引号引出的字段转换为 float 类型。
csv.QUOTE_NONE
指示 writer
对象不使用引号引出字段。当 定界符 出现在输出数据中时,其前面应该有 转义符。如果未设置 转义符,则遇到任何需要转义的字符时,writer 都会抛出 Error
异常。
指示 reader
不对引号字符进行特殊处理。
csv
模块定义了以下异常:
exceptioncsv.Error
该异常可能由任何发生错误的函数抛出。
变种与格式参数
为了更容易指定输入和输出记录的格式,特定的一组格式参数组合为一个 dialect(变种)。一个 dialect 是一个 Dialect
类的子类,它具有一组特定的方法和一个 validate()
方法。创建 reader
或 writer
对象时,程序员可以将某个字符串或 Dialect
类的子类指定为 dialect 参数。要想补充或覆盖 dialect 参数,程序员还可以单独指定某些格式参数,这些参数的名称与下面 Dialect
类定义的属性相同。
Dialect 类支持以下属性:
Dialect.delimiter
一个用于分隔字段的单字符,默认为 ','
。
Dialect.doublequote
控制出现在字段中的 引号字符 本身应如何被引出。当该属性为 True
时,双写引号字符。如果该属性为 False
,则在 引号字符 的前面放置 转义符。默认值为 True
。
在输出时,如果 doublequote 是 False
,且 转义符 未指定,且在字段中发现 引号字符 时,会抛出 Error
异常。
Dialect.escapechar
一个用于 writer 的单字符,用来在 quoting 设置为 QUOTE_NONE
的情况下转义 定界符*,在 *doublequote 设置为 False
的情况下转义 引号字符。在读取时,escapechar 去除了其后所跟字符的任何特殊含义。该属性默认为 None
,表示禁用转义。
Dialect.lineterminator
放在 writer
产生的行的结尾,默认为 '\r\n'
。
注解
reader
经过硬编码,会识别 '\r'
或 '\n'
作为行尾,并忽略 lineterminator。未来可能会更改这一行为。
Dialect.quotechar
一个单字符,用于包住含有特殊字符的字段,特殊字符如 定界符 或 引号字符 或换行符。默认为 '"'
。
Dialect.quoting
控制 writer 何时生成引号,以及 reader 何时识别引号。该属性可以等于任何 QUOTE_*
常量,默认为 QUOTE_MINIMAL
。
Dialect.skipinitialspace
如果为 True
,则忽略 定界符 之后的空格。默认值为 False
。
Dialect.strict
如果为 True
,则在输入错误的 CSV 时抛出 Error
异常。默认值为 False
。
Reader 对象
Reader 对象(DictReader
实例和 reader()
函数返回的对象)具有以下公开方法:
csvreader.__next__
()
返回 reader 的可迭代对象的下一行,它可以是一个列表(如果对象是由 reader()
返回)或字典(如果是一个 DictReader
实例),根据当前 Dialect
来解析。 通常你应当以 next(reader)
的形式来调用它。
Reader 对象具有以下公开属性:
csvreader.dialect
变种描述,只读,供解析器使用。
csvreader.line_num
源迭代器已经读取了的行数。它与返回的记录数不同,因为记录可能跨越多行。
DictReader 对象具有以下公开属性:
csvreader.fieldnames
字段名称。如果在创建对象时未传入字段名称,则首次访问时或从文件中读取第一条记录时会初始化此属性。
Writer 对象
Writer
对象(DictWriter
实例和 writer()
函数返回的对象)具有下面的公开方法。对于 Writer
对象,行 必须是(一组可迭代的)字符串或数字。对于 DictWriter
对象,行 必须是一个字典,这个字典将字段名映射为字符串或数字(数字要先经过 str()
转换类型)。请注意,输出的复数会有括号包围。这样其他程序读取 CSV 文件时可能会有一些问题(假设它们完全支持复数)。
csvwriter.writerow
(row)
将 row 形参写入到 writer 的文件对象,根据当前 Dialect
进行格式化。 返回对下层文件对象的 write 方法的调用的返回值。
在 3.5 版更改: 开始支持任意类型的迭代器。
csvwriter.writerows
(rows)
将 rows*(即能迭代出多个上述 *row 对象的迭代器)中的所有元素写入 writer 的文件对象,并根据当前设置的变种进行格式化。
Writer 对象具有以下公开属性:
csvwriter.dialect
变种描述,只读,供 writer 使用。
DictWriter 对象具有以下公开方法:
DictWriter.writeheader
()
在 writer 的文件对象中,写入一行字段名称(字段名称在构造函数中指定),并根据当前设置的变种进行格式化。本方法的返回值就是内部使用的 csvwriter.writerow()
方法的返回值。
3.2 新版功能.
在 3.8 版更改: 现在 writeheader()
也返回其内部使用的 csvwriter.writerow()
方法的返回值。
例子
读取 CSV 文件最简单的一个例子:
import csv
with open('some.csv', newline='')as f:
reader = csv.reader(f)
for row in reader:
print(row)
读取其他格式的文件:
import csv
with open('passwd', newline='')as f:
reader = csv.reader(f, delimiter=':', quoting=csv.QUOTE_NONE)
for row in reader:
print(row)
相应最简单的写入示例是:
import csv
with open('some.csv','w', newline='')as f:
writer = csv.writer(f)
writer.writerows(someiterable)
由于使用 open()
来读取 CSV 文件,因此默认情况下,将使用系统默认编码来解码文件并转换为 unicode(请参阅 locale.getpreferredencoding()
)。要使用其他编码来解码文件,请使用 open 的 encoding
参数:
import csv
with open('some.csv', newline='', encoding='utf-8')as f:
reader = csv.reader(f)
for row in reader:
print(row)
这同样适用于写入非系统默认编码的内容:打开输出文件时,指定 encoding 参数。
注册一个新的变种:
import csv
csv.register_dialect('unixpwd', delimiter=':', quoting=csv.QUOTE_NONE)
with open('passwd', newline='')as f:
reader = csv.reader(f,'unixpwd')
Reader 的更高级用法——捕获并报告错误:
import csv, sys
filename ='some.csv'
with open(filename, newline='')as f:
reader = csv.reader(f)
try:
for row in reader:
print(row)
except csv.Erroras e:
sys.exit('file {}, line {}: {}'.format(filename, reader.line_num, e))
尽管该模块不直接支持解析字符串,但仍可如下轻松完成:
import csv
for row in csv.reader(['one,two,three']):
print(row)
configparser
—- 配置文件解析器
源代码: Lib/configparser.py
此模块提供了它实现一种基本配置语言 ConfigParser
类,这种语言所提供的结构与 Microsoft Windows INI 文件的类似。 你可以使用这种语言来编写能够由最终用户来自定义的 Python 程序。
注解
这个库 并不 能够解析或写入在 Windows Registry 扩展版本 INI 语法中所使用的值-类型前缀。
快速起步
让我们准备一个非常基本的配置文件,它看起来是这样的:
[DEFAULT]
ServerAliveInterval = 45
Compression = yes
CompressionLevel = 9
ForwardX11 = yes
[bitbucket.org]
User = hg
[topsecret.server.com]
Port = 50022
ForwardX11 = no
总的来说,这种文件由多个节组成,每个节包含多个带有值的键。 configparser
类可以读取和写入这种文件。 让我们先通过程序方式来创建上述的配置文件。
>>> import configparser
>>> config = configparser.ConfigParser()
>>> config['DEFAULT'] = {'ServerAliveInterval': '45',
... 'Compression': 'yes',
... 'CompressionLevel': '9'}
>>> config['bitbucket.org'] = {}
>>> config['bitbucket.org']['User'] = 'hg'
>>> config['topsecret.server.com'] = {}
>>> topsecret = config['topsecret.server.com']
>>> topsecret['Port'] = '50022' # mutates the parser
>>> topsecret['ForwardX11'] = 'no' # same here
>>> config['DEFAULT']['ForwardX11'] = 'yes'
>>> with open('example.ini', 'w') as configfile:
... config.write(configfile)
...
如你所见,我们可以把配置解析器当作一个字典来处理。
现在我们已经创建并保存了一个配置文件,让我们再将它读取出来并探究其中包含的数据。
>>> config = configparser.ConfigParser()
>>> config.sections()
[]
>>> config.read('example.ini')
['example.ini']
>>> config.sections()
['bitbucket.org', 'topsecret.server.com']
>>> 'bitbucket.org' in config
True
>>> 'bytebong.com' in config
False
>>> config['bitbucket.org']['User']
'hg'
>>> config['DEFAULT']['Compression']
'yes'
>>> topsecret = config['topsecret.server.com']
>>> topsecret['ForwardX11']
'no'
>>> topsecret['Port']
'50022'
>>> for key in config['bitbucket.org']:
... print(key)
user
compressionlevel
serveraliveinterval
compression
forwardx11
>>> config['bitbucket.org']['ForwardX11']
'yes'
正如我们在上面所看到的,相关的 API 相当直观。 唯一有些神奇的地方是 DEFAULT
小节,它为所有其他小节提供了默认值 1。 还要注意小节中的键大小写不敏感并且会存储为小写形式。
将多个配置读入单个 ConfigParser
是可能的,其中最近添加的配置具有最高优先级。 任何冲突的键都会从更近的配置获取并且先前存在的键会被保留。
>>> another_config = configparser.ConfigParser()
>>> another_config.read('example.ini')
['example.ini']
>>> another_config['topsecret.server.com']['Port']
'50022'
>>> another_config.read_string("[topsecret.server.com]\nPort=48484")
>>> another_config['topsecret.server.com']['Port']
'48484'
>>> another_config.read_dict({"topsecret.server.com": {"Port": 21212}})
>>> another_config['topsecret.server.com']['Port']
'21212'
>>> another_config['topsecret.server.com']['ForwardX11']
'no'
此行为等价于一次 ConfigParser.read()
调用并向 filenames 形参传入多个文件。
支持的数据类型
配置解析器并不会猜测配置文件中值的类型,而总是将它们在内部存储为字符串。 这意味着如果你需要其他数据类型,你应当自己来转换:
>>> int(topsecret['Port'])
50022
>>> float(topsecret['CompressionLevel'])
9.0
由于这种任务十分常用,配置解析器提供了一系列便捷的获取方法来处理整数、浮点数和布尔值。 最后一个类型的处理最为有趣,因为简单地将值传给 bool()
是没有用的,bool('False')
仍然会是 True
。 为解决这个问题配置解析器还提供了 getboolean()
。 这个方法对大小写不敏感并可识别 'yes'
/'no'
, 'on'
/'off'
, 'true'
/'false'
和 '1'
/'0'
等布尔值。 例如:
>>> topsecret.getboolean('ForwardX11')
False
>>> config['bitbucket.org'].getboolean('ForwardX11')
True
>>> config.getboolean('bitbucket.org', 'Compression')
True
除了 getboolean()
,配置解析器还提供了同类的 getint()
和 getfloat()
方法。 你可以注册你自己的转换器并或是定制已提供的转换器。
回退值
与字典类似,你可以使用某个小节的 get()
方法来提供回退值:
>>> topsecret.get('Port')
'50022'
>>> topsecret.get('CompressionLevel')
'9'
>>> topsecret.get('Cipher')
>>> topsecret.get('Cipher', '3des-cbc')
'3des-cbc'
请注意默认值会优先于回退值。 例如,在我们的示例中 'CompressionLevel'
键仅在 'DEFAULT'
小节被指定。 如果你尝试在 'topsecret.server.com'
小节获取它,我们将总是获取到默认值,即使我们指定了一个回退值:
>>> topsecret.get('CompressionLevel', '3')
'9'
还需要注意的一点是解析器层级的 get()
方法提供了自定义的更复杂接口,它被维护用于向下兼容。 当使用此方法时,回退值可以通过 fallback
仅限关键字参数来提供:
>>> config.get('bitbucket.org', 'monster',
... fallback='No such things as monsters')
'No such things as monsters'
同样的 fallback
参数也可在 getint()
, getfloat()
和 getboolean()
方法中使用,例如:
>>> 'BatchMode' in topsecret
False
>>> topsecret.getboolean('BatchMode', fallback=True)
True
>>> config['DEFAULT']['BatchMode'] = 'no'
>>> topsecret.getboolean('BatchMode', fallback=True)
False
受支持的 INI 文件结构
A configuration file consists of sections, each led by a [section]
header, followed by key/value entries separated by a specific string (=
or :
by default ). By default, section names are case sensitive but keys are not. Leading and trailing whitespace is removed from keys and values. Values can be omitted if the parser is configured to allow it , in which case the key/value delimiter may also be left out. Values can also span multiple lines, as long as they are indented deeper than the first line of the value. Depending on the parser’s mode, blank lines may be treated as parts of multiline values or ignored.
配置文件可以包含注释,要带有指定字符前缀 (默认为 #
和 ;
)。 注释可以单独出现于原本的空白行,并可使用缩进。
例如:
[Simple Values]
key=value
spaces in keys=allowed
spaces in values=allowed as well
spaces around the delimiter = obviously
you can also use : to delimit keys from values
[All Values Are Strings]
values like this: 1000000
or this: 3.14159265359
are they treated as numbers? : no
integers, floats and booleans are held as: strings
can use the API to get converted values directly: true
[Multiline Values]
chorus: I'm a lumberjack, and I'm okay
I sleep all night and I work all day
[No Values]
key_without_value
empty string value here =
[You can use comments]
# like this
; or this
# By default only in an empty line.
# Inline comments can be harmful because they prevent users
# from using the delimiting characters as parts of values.
# That being said, this can be customized.
[Sections Can Be Indented]
can_values_be_as_well = True
does_that_mean_anything_special = False
purpose = formatting for readability
multiline_values = are
handled just fine as
long as they are indented
deeper than the first line
of a value
# Did I mention we can indent comments, too?
值的插值
在核心功能之上,ConfigParser
还支持插值。 这意味着值可以在被 get()
调用返回之前进行预处理。
class configparser.BasicInterpolation
默认实现由 ConfigParser
来使用。 它允许值包含引用了相同小节中其他值或者特殊的默认小节中的值的格式字符串。 额外的默认值可以在初始化时提供。
例如:
[Paths]
home_dir: /Users
my_dir: %(home_dir)s/lumberjack
my_pictures: %(my_dir)s/Pictures
[Escape]
gain: 80%% # use a %% to escape the % sign (% is the only character that needs to be escaped)
在上面的例子里,ConfigParser
的 interpolation 设为 BasicInterpolation()
,这会将 %(home_dir)s
求解为 home_dir
的值 (在这里是 /Users
)。 %(my_dir)s
的将被实际求解为 /Users/lumberjack
。 所有插值都是按需进行的,这样引用链中使用的键不必以任何特定顺序在配置文件中指明。
当 interpolation
设为 None
时,解析器会简单地返回 %(my_dir)s/Pictures
作为 my_pictures
的值,并返回 %(home_dir)s/lumberjack
作为 my_dir
的值。
class configparser.ExtendedInterpolation
一个用于插值的替代处理程序实现了更高级的语法,它被用于 zc.buildout
中的实例。 扩展插值使用 ${section:option}
来表示来自外部小节的值。 插值可以跨越多个层级。 为了方便使用,section:
部分可被省略,插值会默认作用于当前小节(可能会从特殊小节获取默认值)。
例如,上面使用基本插值描述的配置,使用扩展插值将是这个样子:
[Paths]
home_dir: /Users
my_dir: ${home_dir}/lumberjack
my_pictures: ${my_dir}/Pictures
[Escape]
cost: $$80 # use a $$ to escape the $ sign ($ is the only character that needs to be escaped)
来自其他小节的值也可以被获取:
[Common]
home_dir: /Users
library_dir: /Library
system_dir: /System
macports_dir: /opt/local
[Frameworks]
Python: 3.2
path: ${Common:system_dir}/Library/Frameworks/
[Arthur]
nickname: Two Sheds
last_name: Jackson
my_dir: ${Common:home_dir}/twosheds
my_pictures: ${my_dir}/Pictures
python_dir: ${Frameworks:path}/Python/Versions/${Frameworks:Python}
映射协议访问
3.2 新版功能.
映射协议访问这个通用名称是指允许以字典的方式来使用自定义对象的功能。 在 configparser
中,映射接口的实现使用了 parser['section']['option']
标记法。
parser['section']
专门为解析器中的小节数据返回一个代理。 这意味着其中的值不会被拷贝,而是在需要时从原始解析器中获取。 更为重要的是,当值在小节代理上被修改时,它们其实是在原始解析器中发生了改变。
configparser
对象的行为会尽可能地接近真正的字典。 映射接口是完整而且遵循 MutableMapping
ABC 规范的。 但是,还是有一些差异应当被纳入考虑:
默认情况下,小节中的所有键是以大小写不敏感的方式来访问的。 例如
for option in parser["section"]
只会产生optionxform
形式的选项键名称。 也就是说默认使用小写字母键名。 与此同时,对于一个包含键'a'
的小节,以下两个表达式均将返回True
:"a" in parser["section"] "A" in parser["section"]
所有小节也包括
DEFAULTSECT
,这意味着对一个小节执行.clear()
可能无法使得该小节显示为空。 这是因为默认值是无法从小节中被删除的(因为从技术上说它们并不在那里)。 如果它们在小节中被覆盖,删除将导致默认值重新变为可见。 尝试删除默认值将会引发KeyError
。DEFAULTSECT
无法从解析器中被移除:- 尝试删除将引发
ValueError
, parser.clear()
会保留其原状,parser.popitem()
绝不会将其返回。
- 尝试删除将引发
parser.get(section, option, **kwargs)
- 第二个参数 并非 回退值。 但是请注意小节层级的get()
方法可同时兼容映射协议和经典配置解析器 API。parser.items()
兼容映射协议(返回 section_name, section_proxy 对的列表,包括 DEFAULTSECT)。 但是,此方法也可以带参数发起调用:parser.items(section, raw, vars)
。 这种调用形式返回指定section
的 option, value 对的列表,将展开所有插值(除非提供了raw=True
选项)。
映射协议是在现有的传统 API 之上实现的,以便重载原始接口的子类仍然具有符合预期的有效映射。
定制解析器行为
INI 格式的变种数量几乎和使用此格式的应用一样多。 configparser
花费了很大力气来为尽量大范围的可用 INI 样式提供支持。 默认的可用功能主要由历史状况来确定,你很可能会想要定制某些特性。
改变特定配置解析器行为的最常见方式是使用 __init__()
选项:
defaults,默认值:
None
此选项接受一个键值对的字典,它将被首先放入
DEFAULT
小节。 这实现了一种优雅的方式来支持简洁的配置文件,它不必指定与已记录的默认值相同的值。提示:如果你想要为特定的小节指定默认的值,请在读取实际文件之前使用
read_dict()
。dict_type,默认值:
dict
此选项主要影响映射协议的行为和写入配置文件的外观。 使用标准字典时,每个小节是按照它们被加入解析器的顺序保存的。 在小节内的选项也是如此。
还有其他替换的字典类型可以使用,例如在写回数据时对小节和选项进行排序。
请注意:存在其他方式只用一次操作来添加键值对的集合。 当你在这些操作中使用一个常规字典时,键将按顺序进行排列。 例如:
>>> parser = configparser.ConfigParser() >>> parser.read_dict({'section1': {'key1': 'value1', ... 'key2': 'value2', ... 'key3': 'value3'}, ... 'section2': {'keyA': 'valueA', ... 'keyB': 'valueB', ... 'keyC': 'valueC'}, ... 'section3': {'foo': 'x', ... 'bar': 'y', ... 'baz': 'z'} ... }) >>> parser.sections() ['section1', 'section2', 'section3'] >>> [option for option in parser['section3']] ['foo', 'bar', 'baz']
allow_no_value,默认值:
False
已知某些配置文件会包括不带值的设置,但其在其他方面均符合
configparser
所支持的语法。 构造器的 allow_no_value 形参可用于指明应当接受这样的值:>>> import configparser >>> sample_config = """ ... [mysqld] ... user = mysql ... pid-file = /var/run/mysqld/mysqld.pid ... skip-external-locking ... old_passwords = 1 ... skip-bdb ... # we don't need ACID today ... skip-innodb ... """ >>> config = configparser.ConfigParser(allow_no_value=True) >>> config.read_string(sample_config) >>> # Settings with values are treated as before: >>> config["mysqld"]["user"] 'mysql' >>> # Settings without values provide None: >>> config["mysqld"]["skip-bdb"] >>> # Settings which aren't specified still raise an error: >>> config["mysqld"]["does-not-exist"] Traceback (most recent call last): ... KeyError: 'does-not-exist'
delimiters,默认值:
('=', ':')
分隔符是用于在小节内分隔键和值的子字符串。 在一行中首次出现的分隔子字符串会被视为一个分隔符。 这意味着值可以包含分隔符(但键不可以)。
comment_prefixes,默认值:
('#', ';')
inline_comment_prefixes,默认值:
None
注释前缀是配置文件中用于标示一条有效注释的开头的字符串。 comment_prefixes 仅用在被视为空白的行(可以缩进)之前而 inline_comment_prefixes 可用在每个有效值之后(例如小节名称、选项以及空白的行)。 默认情况下禁用行内注释,并且
'#'
和';'
都被用作完整行注释的前缀。在 3.2 版更改: 在之前的
configparser
版本中行为匹配comment_prefixes=('#',';')
和inline_comment_prefixes=(';',)
。请注意配置解析器不支持对注释前缀的转义,因此使用 inline_comment_prefixes 可能妨碍用户将被用作注释前缀的字符指定为可选值。 当有疑问时,请避免设置 inline_comment_prefixes。 在许多情况下,在多行值的一行开头存储注释前缀字符的唯一方式是进行前缀插值,例如:
>>> from configparser import ConfigParser, ExtendedInterpolation >>> parser = ConfigParser(interpolation=ExtendedInterpolation()) >>> # the default BasicInterpolation could be used as well >>> parser.read_string(""" ... [DEFAULT] ... hash = # ... ... [hashes] ... shebang = ... ${hash}!/usr/bin/env python ... ${hash} -*- coding: utf-8 -*- ... ... extensions = ... enabled_extension ... another_extension ... #disabled_by_comment ... yet_another_extension ... ... interpolation not necessary = if # is not at line start ... even in multiline values = line #1 ... line #2 ... line #3 ... """) >>> print(parser['hashes']['shebang']) #!/usr/bin/env python # -*- coding: utf-8 -*- >>> print(parser['hashes']['extensions']) enabled_extension another_extension yet_another_extension >>> print(parser['hashes']['interpolation not necessary']) if # is not at line start >>> print(parser['hashes']['even in multiline values']) line #1 line #2 line #3
strict,默认值:
True
当设为
True
时,解析器在从单一源读取 (使用read_file()
,read_string()
或read_dict()
) 期间将不允许任何小节或选项出现重复。 推荐在新的应用中使用严格解析器。在 3.2 版更改: 在之前的
configparser
版本中行为匹配strict=False
。empty_lines_in_values,默认值:
True
在配置解析器中,值可以包含多行,只要它们的缩进级别低于它们所对应的键。 默认情况下解析器还会将空行视为值的一部分。 于此同时,键本身也可以任意缩进以提升可读性。 因此,当配置文件变得非常庞大而复杂时,用户很容易失去对文件结构的掌控。 例如:
[Section] key = multiline value with a gotcha this = is still a part of the multiline value of 'key'
在用户查看时这可能会特别有问题,如果她是使用比例字体来编辑文件的话。 这就是为什么当你的应用不需要带有空行的值时,你应该考虑禁用它们。 这将使得空行每次都会作为键之间的分隔。 在上面的示例中,空行产生了两个键,
key
和this
。default_section,默认值:
configparser.DEFAULTSECT
(即:"DEFAULT"
)允许设置一个保存默认值的特殊节在其他节或插值等目的中使用的惯例是这个库所拥有的一个强大概念,使得用户能够创建复杂的声明性配置。 这种特殊节通常称为
"DEFAULT"
但也可以被定制为指向任何其他有效的节名称。 一些典型的值包括:"general"
或"common"
。 所提供的名称在从任意节读取的时候被用于识别默认的节,而且也会在将配置写回文件时被使用。 它的当前值可以使用parser_instance.default_section
属性来获取,并且可以在运行时被修改(即将文件从一种格式转换为另一种格式)。interpolation,默认值:
configparser.BasicInterpolation
插值行为可以用通过提供 interpolation 参数提供自定义处理程序的方式来定制。
None
可用来完全禁用插值,ExtendedInterpolation()
提供了一种更高级的变体形式,它的设计受到了zc.buildout
的启发。RawConfigParser
具有默认的值None
。converters,默认值: 不设置
配置解析器提供了可选的值获取方法用来执行类型转换。 默认实现包括
getint()
,getfloat()
以及getboolean()
。 如果还需要其他获取方法,用户可以在子类中定义它们,或者传入一个字典,其中每个键都是一个转换器的名称而每个值都是一个实现了特定转换的可调用对象。 例如,传入{'decimal': decimal.Decimal}
将对解释器对象和所有节代理添加getdecimal()
。 换句话说,可以同时编写parser_instance.getdecimal('section', 'key', fallback=0)
和parser_instance['section'].getdecimal('key', 0)
。如果转换器需要访问解析器的状态,可以在配置解析器子类上作为一个方法来实现。 如果该方法的名称是以
get
打头的,它将在所有节代理上以兼容字典的形式提供(参见上面的getdecimal()
示例)。
更多高级定制选项可通过重载这些解析器属性的默认值来达成。 默认值是在类中定义的,因此它们可以通过子类或属性赋值来重载。
ConfigParser.BOOLEAN_STATES
默认情况下当使用 getboolean()
时,配置解析器会将下列值视为 True
: '1'
, 'yes'
, 'true'
, 'on'
而将下列值视为 False
: '0'
, 'no'
, 'false'
, 'off'
。 你可以通过指定一个自定义的字符串键及其对应的布尔值字典来重载此行为。 例如:
>>> custom = configparser.ConfigParser()
>>> custom['section1'] = {'funky': 'nope'}
>>> custom['section1'].getboolean('funky')
Traceback (most recent call last):
...
ValueError: Not a boolean: nope
>>> custom.BOOLEAN_STATES = {'sure': True, 'nope': False}
>>> custom['section1'].getboolean('funky')
False
其他典型的布尔值对包括 accept
/reject
或 enabled
/disabled
。
ConfigParser.optionxform
(option)
这个方法会转换每次 read, get, 或 set 操作的选项名称。 默认会将名称转换为小写形式。 这也意味着当一个配置文件被写入时,所有键都将为小写形式。 如果此行为不合适则要重载此方法。 例如:
>>> config = """
... [Section1]
... Key = Value
...
... [Section2]
... AnotherKey = Value
... """
>>> typical = configparser.ConfigParser()
>>> typical.read_string(config)
>>> list(typical['Section1'].keys())
['key']
>>> list(typical['Section2'].keys())
['anotherkey']
>>> custom = configparser.RawConfigParser()
>>> custom.optionxform = lambda option: option
>>> custom.read_string(config)
>>> list(custom['Section1'].keys())
['Key']
>>> list(custom['Section2'].keys())
['AnotherKey']
注解
optionxform 函数会将选项名称转换为规范形式。 这应该是一个幂等函数:如果名称已经为规范形式,则应不加修改地将其返回。
ConfigParser.SECTCRE
一个已编译正则表达式会被用来解析节标头。 默认将 [section]
匹配到名称 "section"
。 空格会被视为节名称的一部分,因此 [ larch ]
将被读取为一个名称为 " larch "
的节。 如果此行为不合适则要重载此属性。 例如:
>>> import re
>>> config = """
... [Section 1]
... option = value
...
... [ Section 2 ]
... another = val
... """
>>> typical = configparser.ConfigParser()
>>> typical.read_string(config)
>>> typical.sections()
['Section 1', ' Section 2 ']
>>> custom = configparser.ConfigParser()
>>> custom.SECTCRE = re.compile(r"\[ *(?P<header>[^]]+?) *\]")
>>> custom.read_string(config)
>>> custom.sections()
['Section 1', 'Section 2']
注解
虽然 ConfigParser 对象也使用 OPTCRE
属性来识别选项行,但并不推荐重载它,因为这会与构造器选项 allow_no_value 和 delimiters 产生冲突。
旧式 API 示例
主要出于向下兼容性的考虑,configparser
还提供了一种采用显式 get
/set
方法的旧式 API。 虽然以下介绍的方法存在有效的用例,但对于新项目仍建议采用映射协议访问。 旧式 API 在多数时候都更复杂、更底层并且完全违反直觉。
一个写入配置文件的示例:
import configparser
config = configparser.RawConfigParser()
# Please note that using RawConfigParser's set functions, you can assign
# non-string values to keys internally, but will receive an error when
# attempting to write to a file or when you get it in non-raw mode. Setting
# values using the mapping protocol or ConfigParser's set() does not allow
# such assignments to take place.
config.add_section('Section1')
config.set('Section1', 'an_int', '15')
config.set('Section1', 'a_bool', 'true')
config.set('Section1', 'a_float', '3.1415')
config.set('Section1', 'baz', 'fun')
config.set('Section1', 'bar', 'Python')
config.set('Section1', 'foo', '%(bar)s is %(baz)s!')
# Writing our configuration file to 'example.cfg'
with open('example.cfg', 'w') as configfile:
config.write(configfile)
一个再次读取配置文件的示例:
import configparser
config = configparser.RawConfigParser()
config.read('example.cfg')
# getfloat() raises an exception if the value is not a float
# getint() and getboolean() also do this for their respective types
a_float = config.getfloat('Section1', 'a_float')
an_int = config.getint('Section1', 'an_int')
print(a_float + an_int)
# Notice that the next output does not interpolate '%(bar)s' or '%(baz)s'.
# This is because we are using a RawConfigParser().
if config.getboolean('Section1', 'a_bool'):
print(config.get('Section1', 'foo'))
要获取插值,请使用 ConfigParser
:
import configparser
cfg = configparser.ConfigParser()
cfg.read('example.cfg')
# Set the optional *raw* argument of get() to True if you wish to disable
# interpolation in a single get operation.
print(cfg.get('Section1', 'foo', raw=False)) # -> "Python is fun!"
print(cfg.get('Section1', 'foo', raw=True)) # -> "%(bar)s is %(baz)s!"
# The optional *vars* argument is a dict with members that will take
# precedence in interpolation.
print(cfg.get('Section1', 'foo', vars={'bar': 'Documentation',
'baz': 'evil'}))
# The optional *fallback* argument can be used to provide a fallback value
print(cfg.get('Section1', 'foo'))
# -> "Python is fun!"
print(cfg.get('Section1', 'foo', fallback='Monty is not.'))
# -> "Python is fun!"
print(cfg.get('Section1', 'monster', fallback='No such things as monsters.'))
# -> "No such things as monsters."
# A bare print(cfg.get('Section1', 'monster')) would raise NoOptionError
# but we can also use:
print(cfg.get('Section1', 'monster', fallback=None))
# -> None
默认值在两种类型的 ConfigParser 中均可用。 它们将在当某个选项未在别处定义时被用于插值。
import configparser
# New instance with 'bar' and 'baz' defaulting to 'Life' and 'hard' each
config = configparser.ConfigParser({'bar': 'Life', 'baz': 'hard'})
config.read('example.cfg')
print(config.get('Section1', 'foo')) # -> "Python is fun!"
config.remove_option('Section1', 'bar')
config.remove_option('Section1', 'baz')
print(config.get('Section1', 'foo'