Python-Standard Library


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 接受为参数的任意类型。 (例如,对于默认的加法运算,元素可以是任何可相加的类型包括 DecimalFraction。)

通常,输出的元素数量与输入的可迭代对象是一致的。 但是,如果提供了关键字参数 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)

创建一个迭代器,只返回 iterablepredicateFalse 的元素。如果 predicateNone,返回真值测试为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 是一个计算元素键值函数。如果未指定或为 Nonekey 缺省为恒等函数(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 设置的值很高导致被跳过。如果 stopNone,迭代器耗光为止;否则,在指定的位置停止。与普通的切片不同,islice() 不支持将 startstop ,或 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

如果 startNone,迭代从0开始。如果 stepNone ,步长缺省为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 未指定或为 Noner 默认设置为 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 最常见的用途就是在 mapzip 提供一个常量流:

>>> 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 用来显示 maxsizetyped 的值。 这只是出于显示信息的目的。 改变值没有任何效果。

为了帮助衡量缓存的有效性以及调整 maxsize 形参,被包装的函数会带有一个 cache_info() 函数,它返回一个 named tuple 以显示 hits, misses, maxsizecurrsize

该装饰器也提供了一个用于清理/使缓存失效的函数 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 构造器的 argskeywords 之前。

示例:

>>> 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() 可以与函数之外的可调用对象一同使用。 在 assignedupdated 中命名的任何属性如果不存在于被包装对象则会被忽略(即该函数将不会尝试在包装器函数上设置它们)。 如果包装器函数自身缺少在 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)

ab 之间进行全比较。具体的,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)

对于数字 ab,返回 a + b

operator.and_(a, b)

operator.__and__(a, b)

返回 xy 按位与的结果。

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)

对于数字 ab,返回 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)

返回 ab 按位或的结果。

operator.pos(obj)

operator.__pos__(obj)

返回 obj 取正的结果 (+obj)。

operator.pow(a, b)

operator.__pow__(a, b)

对于数字 ab,返回 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)

返回 ab 按位异或的结果。

适用于序列的操作(其中一些也适用于映射)包括:

operator.concat(a, b)

operator.__concat__(a, b)

对于序列 ab,返回 a + b

operator.contains(a, b)

operator.__contains__(a, b)

返回 b in a 检测的结果。 请注意操作数是反序的。

operator.countOf(a, b)

返回 ba 中的出现次数。

operator.delitem(a, b)

operator.__delitem__(a, b)

移除 a 中索引号为 b 的值。

operator.getitem(a, b)

operator.__getitem__(a, b)

返回 a 中索引为 b 的值。

operator.indexOf(a, b)

返回 ba 中首次出现所在的索引号。

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 其中 ab 为序列。

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。它在运行代码的平台上实例化为 具体路径。

在一些用例中纯路径很有用,例如:

  1. 如果你想要在 Unix 设备上操作 Windows 路径(或者相反)。你不应在 Unix 上实例化一个 WindowsPath,但是你可以实例化 PureWindowsPath
  2. 你只想操作路径但不想实际访问操作系统。在这种情况下,实例化一个纯路径是有用的,因为它们没有任何访问操作系统的操作。

参见

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 的子类,此类以当前系统的路径风格表示路径(实例化为 PosixPathWindowsPath):

>>> Path('setup.py')
PosixPath('setup.py')

pathsegments 参数的指定和 PurePath 相同。

class pathlib.PosixPath(\pathsegments*)

一个 PathPurePosixPath 的子类,此类表示一个非 Windows 文件系统的具体路径:

>>> PosixPath('/etc')
PosixPath('/etc')

pathsegments 参数的指定和 PurePath 相同。

class pathlib.WindowsPath(\pathsegments*)

PathPureWindowsPath 的子类,从类表示一个 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。如果 strictFalse,则路径将被尽可能地解析并且任何剩余部分都会被不检查是否存在地追加。如果在解析路径上发生无限循环,则抛出 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 模块的工具

以下是一个映射了 osPurePath/Path 对应相同的函数的表。

注解

以下函数/方法对并不完全等价。 它们有些虽然具有相互重叠的使用场景,但语义并不相同。 这包括了 os.path.abspath()Path.resolve(),以及 os.path.relpath()PurePath.relative_to()

osos.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 已设置将会被使用,否则 HOMEPATHHOMEDRIVE 将被组合起来使用。 初始的 ~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 版更改: 接受一个 类路径对象 用于 pathpaths

os.path.normcase(path)

规范路径的大小写。在 Windows 上,将路径中的所有字符都转换为小写,并将正斜杠转换为反斜杠。在其他操作系统上返回原路径。

在 3.6 版更改: 接受一个 path-like object。

os.path.normpath(path)

通过折叠多余的分隔符和对上级目录的引用来标准化路径名,所以 A//BA/B/A/./BA/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*)

返回指定文件的规范路径,消除路径中存在的任何符号链接(如果操作系统支持)。

如果一个路径不存在或是遇到了符号链接循环,并且 strictTrue,则会引发 OSError。 如果 strictFalse,则会尽可能地解析路径并添加结果而不检查路径是否存在。

注解

这个函数会模拟操作系统生成规范路径的过程,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 的相对文件路径。 这只是一个路径计算:不会访问文件系统来确认 pathstart 是否存在或其性质。 在 Windows 上,当 pathstart 位于不同驱动器时将引发 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)

如果文件描述符 fp1fp2 指向相同文件,则返回 True

可用性: Unix, Windows。

在 3.2 版更改: 添加了对 Windows 的支持。

在 3.6 版更改: 接受一个 path-like object。

os.path.samestat(stat1, stat2)

如果 stat 元组 stat1stat2 指向相同文件,则返回 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 为空,则 headtail 均为空。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 并且可选参数 modeopenhook 会被忽略。 要指定替代文件列表,请将其作为第一个参数传给 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 语句中被用作上下文管理器。 在这个例子中,inputwith 语句结束后将会被关闭,即使发生了异常也是如此:

with fileinput.input(files=('spam.txt', 'eggs.txt'), encoding="utf-8") as f:
    for line in f:
        process(line)

在 3.2 版更改: 可以被用作上下文管理器。

在 3.8 版更改: 关键字形参 modeopenhook 现在是仅限关键字形参。

在 3.10 版更改: 增加了仅限关键字形参 encodingerrors

下列函数会使用 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 如果给出则必须为一个函数,它接受两个参数 filenamemode*,并相应地返回一个打开的文件类对象。 你不能同时使用 *inplaceopenhook

你可以指定 encodingerrors 来将其传给 open()openhook

FileInput 实例可以在 with 语句中被用作上下文管理器。 在这个例子中,inputwith 语句结束后将会被关闭,即使发生了异常也是如此:

with FileInput(files=('spam.txt', 'eggs.txt')) as input:
    process(input)

在 3.2 版更改: 可以被用作上下文管理器。

3.4 版后已移除: 'rU''U' 模式。

3.8 版后已移除: 对 __getitem__() 方法的支持已弃用。

在 3.8 版更改: 关键字形参 modeopenhook 现在是仅限关键字形参。

在 3.10 版更改: 增加了仅限关键字形参 encodingerrors

可选的原地过滤: 如果传递了关键字参数 inplace=Truefileinput.input()FileInput 构造器,则文件会被移至备份文件并将标准输出定向到输入文件(如果已存在与备份文件同名的文件,它将被静默地替换)。 这使得编写一个能够原地重写其输入文件的过滤器成为可能。 如果给出了 backup 形参 (通常形式为 backup='.<some extension>'),它将指定备份文件的扩展名,并且备份文件会被保留;默认情况下扩展名为 '.bak' 并且它会在输出文件关闭时被删除。 在读取标准输入时原地过滤会被禁用。

此模块提供了以下两种打开文件钩子:

fileinput.hook_compressed(filename, mode, **, encoding=None, errors=None*)

使用 gzipbz2 模块透明地打开 gzip 和 bzip2 压缩的文件(通过扩展名 '.gz''.bz2' 来识别)。 如果文件扩展名不是 '.gz''.bz2',文件会以正常方式打开(即使用 open() 并且不带任何解压操作)。

encodingerrors 值会被传给 io.TextIOWrapper 用于压缩文件以及打开普通文件。

用法示例: fi = fileinput.FileInput(openhook=fileinput.hook_compressed, encoding="utf-8")

在 3.10 版更改: 增加了仅限关键字形参 encodingerrors

fileinput.hook_encoded(encoding, errors=None)

返回一个通过 open() 打开每个文件的钩子,使用给定的 encodingerrors 来读取文件。

使用示例: fi = fileinput.FileInput(openhook=fileinput.hook_encoded("utf-8", "surrogateescape"))

在 3.6 版更改: 添加了可选的 errors 形参。

3.10 版后已移除: 此函数已被弃用,因为 input()FileInput 现在有了 encodingerrors 形参。

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)

比较名为 f1f2 的文件,如果它们似乎相等则返回 True ,否则返回 False

如果 shallow 为真值且两个文件的 os.stat() 签名信息(文件类型、大小和修改时间)一致,则文件会被视为相同。

在其他情况下,如果文件大小或内容不同则它们会被视为不同。

需要注意,没有外部程序被该函数调用,这赋予了该函数可移植性与效率。

该函数会缓存过去的比较及其结果,且在文件的 os.stat() 信息变化后缓存条目失效。所有的缓存可以通过使用 clear_cache() 来清除。

filecmp.cmpfiles(dir1, dir2, common, shallow=True)

比较在两个目录 dir1dir2 中,由 common 所确定名称的文件。

返回三组文件名列表: match, mismatch, errorsmatch 含有相匹配的文件, mismatch 含有那些不匹配的,然后 errors 列出那些未被比较文件的名称。如果文件不存在于两目录中的任一个,或者用户缺少读取它们的权限,又或者因为其他的一些原因而无法比较,那么这些文件将会被列在 errors 中。

参数 shallow 具有同 filecmp.cmp() 一致的含义与默认值。

例如, cmpfiles('a', 'b', ['c', 'd/e']) 将会比较 a/cb/c 以及 a/d/eb/d/e'c''d/e' 将会各自出现在返回的三个列表里的某一个列表中。

filecmp.clear_cache()

清除 filecmp 缓存。如果一个文件过快地修改,以至于超过底层文件系统记录修改时间的精度,那么该函数可能有助于比较该类文件。

3.4 新版功能.

dircmp

class filecmp.dircmp(a, b, ignore=None, hide=None)

创建一个用于比较目录 ab 的新的目录比较对象。 ignore 是需要忽略的文件名列表,且默认为 filecmp.DEFAULT_IGNOREShide 是需要隐藏的文件名列表,且默认为 [os.curdir, os.pardir]

dircmp 类如 filecmp.cmp() 中所描述的那样对文件进行 shallow 比较。

dircmp 类提供以下方法:

  • report()

    ab 之间的比较结果打印(到 sys.stdout )。

  • report_partial_closure()

    打印 ab 及共同直接子目录的比较结果。

  • report_full_closure()

    打印 ab 及共同子目录比较结果(递归地)。

dircmp 类提供了一些有趣的属性,用以得到关于参与比较的目录树的各种信息。

需要注意,通过 __getattr__() 钩子,所有的属性将会惰性求值,因此如果只使用那些计算简便的属性,将不会有速度损失。

  • left

    目录 a

  • right

    目录 b

  • left_list

    hideignore 过滤,目录 a 中的文件与子目录。

  • right_list

    hideignore 过滤,目录 b 中的文件与子目录。

  • common

    同时存在于目录 ab 中的文件和子目录。

  • left_only

    仅在目录 a 中的文件和子目录。

  • right_only

    仅在目录 b 中的文件和子目录。

  • common_dirs

    同时存在于目录 ab 中的子目录。

  • common_files

    同时存在于目录 ab 中的文件。

  • common_funny

    在目录 ab 中类型不同的名字,或者那些 os.stat() 报告错误的名字。

  • same_files

    在目录 ab 中,使用类的文件比较操作符判定相等的文件。

  • diff_files

    在目录 ab 中,根据类的文件比较操作符判定内容不等的文件。

  • funny_files

    在目录 ab 中无法比较的文件。

  • subdirs

    一个将 common_dirs 中的名称映射到 dircmp 实例(或者 MyDirCmp 实例,如果该实例类型为 dircmp 的子类 MyDirCmp 的话)的字典。

    在 3.10 版更改: 在之前版本中字典条目总是为 dircmp 实例。 现在条目将与 self 的类型相同,如果 selfdircmp 的子类的话。

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


该模块用于创建临时文件和目录,它可以跨平台使用。TemporaryFileNamedTemporaryFileTemporaryDirectorySpooledTemporaryFile 是带有自动清理功能的高级接口,可用作上下文管理器。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',所以创建的文件不用关闭,就可以读取或写入。因为用的是二进制模式,所以无论存的是什么数据,它在所有平台上都表现一致。bufferingencodingerrorsnewline 的含义与 open() 中的相同。

参数 dirprefixsuffix 的含义和默认值都与它们在 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.BytesIOio.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 则使用默认目录。默认目录是从一个列表中选择出来的,这个列表不同平台不一样,但是用户可以设置 TMPDIRTEMPTMP 环境变量来设置目录的位置。因此,不能保证生成的临时文件路径很规范,比如,通过 os.popen() 将路径传递给外部命令时仍需要加引号。

如果 suffixprefixdir 中的任何一个不是 None,就要保证它们是同一数据类型。如果它们是 bytes,则返回的名称的类型就是 bytes 而不是 str。如果确实要用默认参数,但又想要返回值是 bytes 类型,请传入 suffix=b''

如果指定了 text 且为真值,文件会以文本模式打开。 否则,文件(默认)会以二进制模式打开。

mkstemp() 返回一个元组,元组中第一个元素是句柄,它是一个系统级句柄,指向一个打开的文件(等同于 os.open() 的返回值),第二元素是该文件的绝对路径。

引发一个 tempfile.mkstemp 审计事件,附带参数 fullpath

在 3.5 版更改: 现在,suffixprefixdir 可以以 bytes 类型按顺序提供,以获得 bytes 类型的返回值。之前只允许使用 str。suffixprefix 现在可以接受 None,并且默认为 None 以使用合适的默认值。

在 3.6 版更改: dir 参数现在可接受一个路径类对象 (path-like object)。

tempfile.mkdtemp(suffix=None, prefix=None, dir=None)

以最安全的方式创建一个临时目录,创建该目录时不会有竞争的情况。该目录只能由创建者读取、写入和搜索。

mkdtemp() 用户用完临时目录后需要自行将其删除。

prefixsuffixdir 的含义与它们在 mkstemp() 中的相同。

mkdtemp() 返回新目录的绝对路径。

引发一个 tempfile.mkdtemp 审计事件,附带参数 fullpath

在 3.5 版更改: 现在,suffixprefixdir 可以以 bytes 类型按顺序提供,以获得 bytes 类型的返回值。之前只允许使用 str。suffixprefix 现在可以接受 None,并且默认为 None 以使用合适的默认值。

在 3.6 版更改: dir 参数现在可接受一个路径类对象 (path-like object)。

tempfile.gettempdir()

返回放置临时文件的目录的名称。这个方法的返回值就是本模块所有函数的 dir 参数的默认值。

Python 搜索标准目录列表,以找到调用者可以在其中创建文件的目录。这个列表是:

  1. TMPDIR 环境变量指向的目录。
  2. TEMP 环境变量指向的目录。
  3. TMP 环境变量指向的目录。
  4. 与平台相关的位置:
    • 在 Windows 上,依次为 C:\TEMPC:\TMP\TEMP\TMP
    • 在所有其他平台上,依次为 /tmp/var/tmp/usr/tmp
  5. 不得已时,使用当前工作目录。

在 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() 外的上述任何函数时 tempdirNone (默认值) 则它会按照 gettempdir() 中所描述的算法来初始化。

注解

请注意如果你将 tempdir 设为字节串值,会有一个麻烦的副作用: mkstemp()mkdtemp() 的全局默认返回类型会在没有显式提供字符串类型的when no explicit prefix, suffixdir 的时候被改为字节串。 请不要编写预期或依赖于此入围的代码。 这个笨拙行为是为了保持与历史实现的兼容性。

例子

以下是 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() 来代替。

返回一个绝对路径,这个路径指向的文件在调用本方法时不存在。prefixsuffixdir 参数与 mkstemp() 中的同名参数类似,不同之处在于不支持字节类型的文件名,不支持 suffix=Noneprefix=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.sepos.altsep 则将不匹配文件。

引发一个 审计事件 glob.glob 附带参数 pathname, recursive

引发一个 审计事件 glob.glob/2,附带参数 pathname, recursive, root_dir, dir_fd

注解

在一个较大的目录树中使用 “**“ 模式可能会消耗非常多的时间。

在 3.5 版更改: 支持使用 “**“ 的递归 glob。

在 3.10 版更改: 添加了 root_dirdir_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_dirdir_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 字符串,返回 TrueFalse。 两个形参都会使用 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,返回 TrueFalse;此比较是大小写敏感的,并且不会应用 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_globalsNone。 这可以避免在实际需要读取行之前执行 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*。 *srcdst 均为路径类对象或以字符串形式给出的路径名。

dst 必须是完整的目标文件名。 如果 srcdst 指定了同一个文件,则将引发 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*。 文件的内容、所有者和分组将不受影响。 *srcdst 均为路径类对象或字符串形式的路径名。 如果 follow_symlinks 为假值,并且 srcdst 均为符号链接,copymode() 将尝试修改 dst 本身的模式(而非它所指向的文件)。 此功能并不是在所有平台上均可用;请参阅 copystat() 了解详情。 如果 copymode() 无法修改本机平台上的符号链接,而它被要求这样做,它将不做任何操作即返回。

引发一个 审计事件 shutil.copymode 附带参数 src, dst

在 3.3 版更改: 加入 follow_symlinks 参数。

shutil.copystat(src, dst, **, follow_symlinks=True*)

src 拷贝权限位、最近访问时间、最近修改时间以及旗标到 dst*。 在 Linux上,copystat() 还会在可能的情况下拷贝“扩展属性”。 文件的内容、所有者和分组将不受影响。 *srcdst 均为路径类对象或字符串形式的路径名。

如果 follow_symlinks 为假值,并且 srcdst 均指向符号链接,copystat() 将作用于符号链接本身而非该符号链接所指向的文件 — 从 src 符号链接读取信息,并将信息写入 dst 符号链接。

注解

并非所有平台者提供检查和修改符号链接的功能。 Python 本身可以告诉你哪些功能是在本机上可用的。

  • 如果 os.chmod in os.supports_follow_symlinksTrue,则 copystat() 可以修改符号链接的权限位。
  • 如果 os.utime in os.supports_follow_symlinksTrue,则 copystat() 可以修改符号链接的最近访问和修改时间。
  • 如果 os.chflags in os.supports_follow_symlinksTrue,则 copystat() 可以修改符号链接的旗标。 (os.chflags 不是在所有平台上均可用。)

在此功能部分或全部不可用的平台上,当被要求修改一个符号链接时,copystat() 将尽量拷贝所有内容。 copystat() 一定不会返回失败信息。

引发一个 审计事件 shutil.copystat 附带参数 src, dst

在 3.3 版更改: 添加了 follow_symlinks 参数并且支持 Linux 扩展属性。

shutil.copy(src, dst, **, follow_symlinks=True*)

将文件 src 拷贝到文件或目录 dst*。 *srcdst 应为 路径类对象 或字符串。 如果 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, pathexcinfo

第一个形参 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*,则它必须为接受两个参数 *srcdst 的可调用对象,并将在 os.rename() 无法使用时被用来将 src 拷贝到 dst*。 如果源是一个目录,则会调用 copytree(),并向它传入 copy_function()。 默认的 *copy_functioncopy2()。 使用 copy() 作为 copy_function 允许在无法附带拷贝元数据时让移动操作成功执行,但其代价是不拷贝任何元数据。

引发一个 审计事件 shutil.move 附带参数 src, dst

在 3.3 版更改: 为异类文件系统添加了显式的符号链接处理,以便使它适应 GNU 的 mv 的行为。 现在会返回 dst

在 3.5 版更改: 增加了 copy_function 关键字参数。

在 3.8 版更改: 可能会在内部使用平台专属的快速拷贝系统调用以更高效地拷贝文件。

在 3.9 版更改: 接受一个 path-like object 作为 srcdst

shutil.disk_usage(path)

返回给定路径的磁盘使用统计数据,形式为一个 named tuple,其中包含 total, usedfree 属性,分别表示总计、已使用和未使用空间的字节数。 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 格式的支持。

本模块也提供了用于创建和读取压缩和归档文件的高层级工具。 它们依赖于 zipfiletarfile 模块。

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_dirbase_dir 默认均为当前目录。

如果 dry_run 为真值,则不会创建归档文件,但将要被执行的操作会被记录到 logger

ownergroup 将在创建 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_runlogger (与向 make_archive() 传入的参数一致)。

如果给出了 extra_args,则其应为一个 (name, value) 对的序列,将在归档器可调用对象被使用时作为附加的关键字参数。

descriptionget_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 作为 filenameextract_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)

获取终端窗口的尺寸。

对于两个维度中的每一个,会分别检查环境变量 COLUMNSLINES。 如果定义了这些变量并且其值为正整数,则将使用这些值。

如果未定义 COLUMNSLINES,这是通常的情况,则连接到 sys.__stdout__ 的终端将通过发起调用 os.get_terminal_size() 被查询。

如果由于系统不支持查询,或是由于我们未连接到某个终端而导致查询终端尺寸不成功,则会使用在 fallback 形参中给出的值。 fallback 默认为 (80, 24),这是许多终端模拟器所使用的默认尺寸。

返回的值是一个 os.terminal_size 类型的具名元组。

3.3 新版功能.

数据持久化

picklemarshal 模块可以将许多 Python 数据类型转换为字节流,然后从字节中重新创建对象。 各种与 DBM 相关的模块支持一系列基于散列的文件格式,这些格式存储字符串到其他字符串的映射。

本章中描述的模块列表是:

  • pickle —- Python 对象序列化
    • 与其他 Python 模块间的关系
      • marshal 间的关系
      • json 模块的比较
    • 数据流格式
    • 模块接口
    • 可以被封存/解封的对象
    • 封存类实例
      • 持久化外部对象
      • Dispatch 表
      • 处理有状态的对象
    • 类型,函数和其他对象的自定义归约
    • 外部缓冲区
      • 提供方 API
      • 使用方 API
      • 示例
    • 限制全局变量
    • 性能
    • 例子
  • 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() 函数。但是,如果要对序列化和反序列化加以更多的控制,可以分别创建 PicklerUnpickler 对象。

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)

参数 fileprotocolfix_importsbuffer_callback 的含义与它们在 Pickler 的构造函数中的含义相同。

在 3.8 版更改: 加入了 buffer_callback 参数。

pickle.dumps(obj, protocol=None, **, fix_imports=True, buffer_callback=None*)

obj 封存以后的对象作为 bytes 类型直接返回,而不是将其写入到文件。

参数 protocolfix_importsbuffer_callback 的含义与它们在 Pickler 的构造函数中的含义相同。

在 3.8 版更改: 加入了 buffer_callback 参数。

pickle.load(file, **, fix_imports=True, encoding=’ASCII’, errors=’strict’, buffers=None*)

从已打开的 file object文件 中读取封存后的对象,重建其中特定对象的层次结构并返回。它相当于 Unpickler(file).load()

Pickle 协议版本是自动检测出来的,所以不需要参数来指定协议。封存对象以外的其他字节将被忽略。

参数 filefix_importsencodingerrorsstrictbuffers 的含义与它们在 Unpickler 的构造函数中的含义相同。

在 3.8 版更改: 加入了 buffers 参数。

pickle.loads(data, /, **, fix_imports=True, encoding=”ASCII”, errors=”strict”, buffers=None*)

重建并返回一个对象的封存表示形式 data 的对象层级结构。 data 必须为 bytes-like object。

Pickle 协议版本是自动检测出来的,所以不需要参数来指定协议。封存对象以外的其他字节将被忽略。

参数 filefix_importsencodingerrorsstrictbuffers 的含义与它们在 Unpickler 的构造函数中的含义相同。

在 3.8 版更改: 加入了 buffers 参数。

pickle 模块定义了以下 3 个异常:

exceptionpickle.PickleError

其他 pickle 异常的基类。它是 Exception 的一个子类。

exceptionpickle.PicklingError

Pickler 遇到无法解封的对象时抛出此错误。它是 PickleError 的子类。

exceptionpickle.UnpicklingError

当解封出错时抛出此异常,例如数据损坏或对象不安全。它是 PickleError 的子类。

注意,解封时可能还会抛出其他异常,包括(但不限于) AttributeError、EOFError、ImportError 和 IndexError。

pickle 模块包含了 3 个类,PicklerUnpicklerPickleBuffer

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() 返回 Noneobj 会被照常 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, encodingerrors*,用于控制由Python 2 生成的 pickle 流的兼容性。如果 *fix_imports 为 True,则 pickle 将尝试将旧的 Python 2 名称映射到 Python 3 中对应的新名称。encodingerrors 参数告诉 pickle 如何解码 Python 2 存储的 8 位字符串实例;这两个参数默认分别为 ‘ASCII’ 和 ‘strict’。encoding 参数可置为 ‘bytes’ 来将这些 8 位字符串实例读取为字节对象。读取 NumPy array 和 Python 2 存储的 datetimedatetime 实例时,请使用 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 的对象,其中 modulename 参数都是 str 对象。注意,不要被这个函数的名字迷惑, find_class() 同样可以用来导入函数。

    子类可以重载此方法,来控制加载对象的类型和加载对象的方式,从而尽可能降低安全风险。

    引发一个 审计事件pickle.find_class 附带参数 modulename

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 占用的底层缓冲区。

可以被封存/解封的对象

下列类型可以被封存:

  • NoneTrueFalse
  • 整数、浮点数、复数
  • 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_tablereducer_override(),则 reducer_override() 方法具有优先权。

注解

出于性能理由,可能不会为以下对象调用 reducer_override(): None, True, False, 以及 int, float, bytes, str, dict, set, frozenset, listtuple 的具体实例。

以下是一个简单的例子,其中我们允许封存并重新构建一个给定的类:

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 模块提供了可在封存特定对象时使用的一种定义函数方式。 picklecopy 模块会在封存/拷贝特定对象时使用这些函数。 此模块提供了非类对象构造器的相关配置信息。 这样的构造器可以是工厂函数或类实例。

copyreg.constructor(object)

object 声明为一个有效的构造器。 如果 object 是不可调用的(因而不是一个有效的构造器)则会引发 TypeError

copyreg.pickle(type, function, constructor=None)

声明该 function 应当被用作 type 类型对象的“归约函数”。 function 应当返回字符串或包含两到三个元素的元组。

如果提供了可选的 constructor 形参,它应当是一个可用来重建相应对象的可调用对象,在调用该对象时应传入由 function 所返回的参数元组。 如果 object 是一个类或 constructor 是不可调用的则将引发 TypeError

请查看 pickle 模块了解 functionconstructor 所要求的接口的详情。 请注意一个 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.ndbmdbm.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, writebackkeyencoding 形参具有与 Shelf 类相同的含义。

class shelve.DbfilenameShelf(filename, flag=’c’, protocol=None, writeback=False)

Shelf 的一个子类,它接受一个 filename 而非字典类对象。 下层文件将使用 dbm.open() 来打开。 默认情况下,文件将以读写模式打开。 可选的 flag 形参具有与 open() 函数相同的含义。 可选的 protocolwriteback 形参具有与 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, ValueErrorTypeError。 文件必须为可读的 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, ValueErrorTypeError。 输入的额外字节串会被忽略。

引发一个 审计事件 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.gnudbm.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.gnudbm.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 或更新的版本。

参见

https://www.sqlite.org

SQLite的主页;它的文档详细描述了它所支持的 SQL 方言的语法和可用的数据类型。

https://www.w3schools.com/sql/

学习 SQL 语法的教程、参考和例子。

PEP 249 - DB-API 2.0 规范

PEP 由 Marc-André Lemburg 撰写。

SQLite 常规使用

模块函数和常量

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_DECLTYPESPARSE_COLNAMES 组合来启用类型检测。 由于 SQLite 的行为,生成的字段类型 (例如 max(data)) 不能被检测,即使在设置了 detect_types 形参时也是如此。 在此情况下,返回的类型为 str

默认情况下,check_same_threadTrue,只有当前的线程可以使用该连接。 如果设置为 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 类必须实现两个方法:stepfinalizestep 方法接受 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)

    使用 namecallable 创建排序规则。这个 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 the Cursor.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 to str and the sqlite3 module will return str objects for TEXT. If you want to return bytes instead, you can set it to bytes.

    您还可以将其设置为接受单个 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 实例。

    默认情况下,或者当 pages0 或负整数时,整个数据库将在一个步骤中复制;否则该方法一次循环复制 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() 方法执行 INSERTREPLACE 语句时会被设置。 对于 INSERTREPLACE 以外的操作或者当 executemany() 被调用时,lastrowid 会被设为 None

    如果 INSERTREPLACE 语句操作失败则将返回上一次成功操作的 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 原生支持如下的类型: NULLINTEGERREALTEXTBLOB

因此可以将以下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.datedatetime.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 模式意味着修改数据库的操作会立即生效。 BEGINSAVEPOINT 语句会禁用 autocommit 模式,而用于结束外层事务的 COMMIT, ROLLBACKRELEASE 则会恢复 autocommit 模式。

Python 的 sqlite3 模块默认会在数据修改语言 (DML) 类语句 (即 INSERT/UPDATE/DELETE/REPLACE) 之前隐式地执行一条 BEGIN 语句。

你可以控制 sqlite3 隐式执行的 BEGIN 语句的种类,具体做法是通过将 isolation_level 形参传给 connect() 调用,或者通过指定连接的 isolation_level 属性。 如果你没有指定 isolation_level,将使用基本的 BEGIN,它等价于指定 DEFERRED。 其他可能的值为 IMMEDIATEEXCLUSIVE

你可以禁用 sqlite3 模块的隐式事务管理,具体做法是将 isolation_level 设为 None。 这将使得下层的 sqlite3 库采用 autocommit 模式。 随后你可以通过在代码中显式地使用 BEGIN, ROLLBACK, SAVEPOINTRELEASE 语句来完全控制事务状态。

请注意 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 为整数,可取值为 09-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 为压缩等级,是整数,可取值为 09-11 (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 指定内部压缩操作时所占用内存大小。参数取 19。更大的值占用更多的内存,同时速度也更快输出也更小。

参数 strategy 用于调节压缩算法。可取值为 Z_DEFAULT_STRATEGYZ_FILTEREDZ_HUFFMAN_ONLYZ_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 版更改: wbitsbufsize 可用作关键字参数。

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_FLUSHZ_PARTIAL_FLUSHZ_SYNC_FLUSHZ_FULL_FLUSHZ_BLOCK (zlib 1.2.3.4) 或 Z_FINISH。默认值为 Z_FINISHZ_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 新版功能.

参见

http://www.zlib.net

zlib 库项目主页。

http://www.zlib.net/manual.html

zlib 库用户手册。提供了库的许多功能的解释和用法。

gzip —- 对 gzip 格式的支持

源代码: Lib/gzip.py


此模块提供的简单接口帮助用户压缩和解压缩文件,功能类似于 GNU 应用程序 gzipgunzip

数据压缩由 zlib 模块提供。

gzip 模块提供 GzipFile 类和 open()compress()decompress() 几个便利的函数。GzipFile 类可以读写 gzip 格式的文件,还能自动压缩和解压缩数据,这让操作压缩文件如同操作普通的 file object 一样方便。

注意,此模块不支持部分可以被 gzipgunzip 解压的格式,如利用 compresspack 压缩所得的文件。

这个模块定义了以下内容:

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, errorsnewline 三个参数一定不要设置。

对于文本模式,将会创建一个 GzipFile 对象,并将它封装到一个 io.TextIOWrapper 实例中, 这个实例默认了指定编码,错误抓获行为和行。

在 3.3 版更改: 支持 filename 为一个文件对象,支持文本模式和 encoding, errorsnewline 参数。

在 3.4 版更改: 支持 'x', 'xb'‘xt’ 三种模式。

在 3.6 版更改: 接受一个 path-like object。

exception gzip.BadGzipFile

针对无效 gzip 文件引发的异常。 它继承自 OSError。 针对无效 gzip 文件也可能引发 EOFErrorzlib.error

3.8 新版功能.

class gzip.GzipFile(filename=None, mode=None, compresslevel=9, fileobj=None, mtime=None)

GzipFile 类的构造器支持 truncate() 的异常,与 file object 的大多数方法非常相似。fileobjfilename 至少有一个不为空。

新的实例基于 fileobj*,它可以是一个普通文件,一个 io.BytesIO 对象,或者任何一个与文件相似的对象。当 *filename 是一个文件对象时,它的默认值是 None

fileobjNone 时, filename 参数只用于 gzip 文件头中,这个文件有可能包含未压缩文件的源文件名。如果文件可以被识别,默认 fileobj 的文件名;否则默认为空字符串,在这种情况下文件头将不包含源文件名。

mode 参数可以是 'r', 'rb', 'a', 'ab', 'w', 'wb', 'x''xb' 中的一个,具体取决于文件将被读取还是被写入。 如果可识别则默认为 fileobj 的模式;否则默认为 'rb'。 在未来的 Python 发布版中将不再使用 fileobj 的模式。 最好总是指定 mode 为写入模式。

需要注意的是,文件默认使用二进制模式打开。如果要以文本模式打开文件一个压缩文件,请使用 open() 方法(或者使用 io.TextIOWrapper 包装 GzipFile )。

compresslevel 参数是一个从 09 的整数,用于控制压缩等级;1 最快但压缩比例最小,9 最慢但压缩比例最大。 0 不压缩。默认为 9

mtime 参数是一个可选的数字时间戳用于写入流的最后修改字段,。mtime 只在压缩模式中使用。如果省略或者值为 None,则使用当前时间。

调用 GzipFileclose() 方法不会关闭 fileobj,因为你可以希望增加其它内容到已经压缩的数中。你可以将一个 io.BytesIO 对象作为 fileobj,也可以使用 io.BytesIOgetvalue() 方法从内存缓存中恢复数据。

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 语句,构造器参数 mtimemtime 属性。

在 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 对象。 *compresslevelmtime 的含义与上文中 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 类。
  • 用于增量压缩和解压的 BZ2CompressorBZ2Decompressor 类。
  • 用于一次性压缩和解压的 compress()decompress() 函数。

文件压缩和解压

bz2.open(filename, mode=’rb’, compresslevel=9, encoding=None, errors=None, newline=None)

以二进制或文本模式打开 bzip2 压缩文件,返回一个 file object。

BZ2File 的构造函数类似,filename 参数可以是一个实际的文件名(strbytes 对象),或是已有的可供读取或写入的文件对象。

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, errorsnewline 参数。

对于文本模式,将会创建一个 BZ2File 对象,并将它包装到一个 io.TextIOWrapper 实例中,此实例带有指定的编码格式、错误处理行为和行结束符。

3.3 新版功能.

在 3.4 版更改: 添加了 'x' (单独创建) 模式。

在 3.6 版更改: 接受一个 path-like object。

class bz2.BZ2File(filename, mode=’r’, **, compresslevel=9*)

用二进制模式打开 bzip2 压缩文件。

如果 filename 是一个 strbytes 对象,则打开名称对应的文件目录。 否则的话,filename 应当是一个 file object,它将被用来读取或写入压缩数据。

mode 参数可以是表示读取的 'r' (默认值),表示覆写的 'w',表示单独创建的 'x',或表示添加的 'a'。 这些模式还可分别以 'rb', 'wb', 'xb''ab' 的等价形式给出。

如果 filename 是一个文件对象(而不是实际的文件名),则 'w' 模式并不会截断文件,而是会等价于 'a'

如果 mode'w''a',则 compresslevel 可以是 19 之间的整数,用于指定压缩等级: 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 版更改: 这个类在面对多个同时读取器和写入器时是线程安全的,就如它在 gziplzma 中的等价类所具有的特性一样。

增量压缩和解压

class bz2.BZ2Compressor(compresslevel=9)

创建一个新的压缩器对象。 此对象可被用来执行增量数据压缩。 对于一次性压缩,请改用 compress() 函数。

如果给定 compresslevel,它必须为 19 之间的整数。 默认值为 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,它必须为 19 之间的整数。 默认值为 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 模块的非常类似。 请注意 LZMAFilebz2.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"

当打开一个文件用于读取时,formatfilters 参数具有与 LZMADecompressor 的参数相同的含义。 在此情况下,checkpreset 参数不应被使用。

当打开一个文件用于写入的,format, check, presetfilters 参数具有与 LZMACompressor 的参数相同的含义。

对于二进制模式,这个函数等价于 LZMAFile 构造器: LZMAFile(filename, mode, ...)。 在这种情况下,不可提供 encoding, errorsnewline 参数。

对于文本模式,将会创建一个 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"

当打开一个文件用于读取时,输入文件可以为多个独立压缩流的拼接。 它们会被作为单个逻辑流被透明地解码。

当打开一个文件用于读取时,formatfilters 参数具有与 LZMADecompressor 的参数相同的含义。 在此情况下,checkpreset 参数不应被使用。

当打开一个文件用于写入的,format, check, presetfilters 参数具有与 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_ALONEFORMAT_RAW 的默认值(也是唯一可接受的值)。
  • CHECK_CRC32: 32 位循环冗余检查。
  • CHECK_CRC64: 64 位循环冗余检查。 这是 FORMAT_XZ 的默认值。
  • CHECK_SHA256: 256 位安全哈希算法。

如果指定的检查不受支持,则会引发 LZMAError

压缩设置可被指定为一个预设的压缩等级(通过 preset 参数)或以自定义过滤器链来详细设置(通过 filters 参数)。

preset 参数(如果提供)应当为一个 09 (包括边界) 之间的整数,可以选择与常数 PRESET_EXTREME 进行 OR 运算。 如果 presetfilters 均未给出,则默认行为是使用 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_ALONEFORMAT_RAW

memlimit 参数指定解压缩器可以使用的内存上限(字节数)。 当使用此参数时,如果不可能在给定内存上限之内解压缩输入数据则解压缩将失败并引发 LZMAError

filters 参数指定用于创建被解压缩数据流的过滤器链。 此参数在 formatFORMAT_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, presetfilters 参数的说明。

lzma.decompress(data, format=FORMAT_AUTO, memlimit=None, filters=None)

解压缩 data (一个 bytes 对象),返回包含解压缩数据的 bytes 对象。

如果 data 是多个单独压缩数据流的拼接,则解压缩所有相应数据流,并返回结果的拼接。

参见上文的 LZMADecompressor 了解有关 format, memlimitfilters 参数的说明。

杂项

lzma.is_check_supported(check)

如果本系统支持给定的一致性检查则返回 True

CHECK_NONECHECK_CRC32 总是受支持。 CHECK_CRC64CHECK_SHA256 或许不可用,如果你正在使用基于受限制特性集编译的 liblzma 版本的话。

指定自定义的过滤器链

过滤器链描述符是由字典组成的序列,其中每个字典包含单个过滤器的 ID 和选项。 每个字典必须包含键 "id",并可能包含额外的键用来指定基于过滤器的选项。 有效的过滤器 ID 如下:

  • 压缩过滤器:
    • FILTER_LZMA1 (配合 FORMAT_ALONE 使用)
    • FILTER_LZMA2 (配合 FORMAT_XZFORMAT_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_FASTMODE_NORMAL
  • nice_len: 对于一个匹配应当被视为“适宜长度”的值。 这应当小于或等于 273。
  • mf: 要使用的匹配查找器 — MF_HC3, MF_HC4, MF_BT2, MF_BT3MF_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,否则返回 Falsefilename 也可能是一个文件或类文件对象。

在 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 文件,或者无法提取单个文件。

参见

PKZIP 应用程序笔记

Phil Katz 编写的 ZIP 文件格式文档,此格式和使用的算法的创建者。

Info-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_BZIP2ZIP_LZMA;不可识别的值将导致引发 NotImplementedError。 如果指定了 ZIP_DEFLATED, ZIP_BZIP2ZIP_LZMA 但相应的模块 (zlib, bz2lzma) 不可用,则会引发 RuntimeError。 默认值为 ZIP_STORED

如果 allowZip64True (默认值) 则当 zipfile 大于 4 GiB 时 zipfile 将创建使用 ZIP64 扩展的 ZIP 文件。 如果该参数为 false 则当 ZIP 文件需要 ZIP64 扩展时 zipfile 将引发异常。

compresslevel 形参控制在将文件写入归档时要使用的压缩等级。 当使用 ZIP_STOREDZIP_LZMA 时无压缩效果。 当使用 ZIP_DEFLATED 时接受整数 09。 当使用 ZIP_BZIP2 时接受整数 19

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 版更改: 添加了对 bzip2lzma 压缩的支持。

在 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_BZIP2ZIP_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*,它可以是一个 strbytes 的实例;如果是 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 文件并将相应的文件添加到归档。

    如果 PyZipFileoptimize 形参未给定或为 -1,则相应的文件为 *.pyc 文件,并在必要时进行编译。

    如果 PyZipFileoptimize 形参为 0, 12,则限具有相应优化级别的文件会被添加到归档,并在必要时进行编译。

    如果 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, bz2lzma 解压的归档要求相应的模块可用。
  • 支持读取 / 写入 POSIX.1-1988 (ustar) 格式。
  • 对 GNU tar 格式的读/写支持,包括 longnamelonglink 扩展,对所有种类 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 归档文件则返回 Truename 可以为 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_FORMATPAX_FORMAT 中的一个。 当读取时,格式将被自动检测,即使单个归档中存在不同的格式。

tarinfo 参数可以被用来将默认的 TarInfo 类替换为另一个。

如果 dereferenceFalse,则会将符号链接和硬链接添加到归档中。 如果为 True,则会将目标文件的内容添加到归档中。 在不支持符号链接的系统上参数将不起作用。

如果 ignore_zerosFalse,则会将空的数据块当作归档的末尾来处理。 如果为 True,则会跳过空的(和无效的)数据块并尝试获取尽可能多的成员。 此参数仅适用于读取拼接的或损坏的归档。

debug 可设为从 0 (无调试消息) 到 3 (全部调试消息)。 消息会被写入到 sys.stderr

如果 errorlevel0,则当使用 TarFile.extract() 时会忽略所有错误。 无论何种情况,当启用调试时它们都将被显示为调试输出的错误消息。 如果为 1,则所有 fatal 错误会被作为 OSError 异常被引发。 如果为 2,则所有 non-fatal 错误也会被作为 TarError 异常被引发。

encodingerrors 参数定义了读取或写入归档所使用的字符编码格式以及要如何处理转换错误。 默认设置将适用于大多数用户。

可选的 pax_headers 参数是字符串的字典,如果 formatPAX_FORMAT 它将被作为 pax 全局标头被添加。

在 3.2 版更改: 使用 'surrogateescape' 作为 errors 参数的默认值。

在 3.5 版更改: 添加了 'x' (单独创建) 模式。

在 3.6 版更改: name 形参接受一个 path-like object。

classmethod TarFile.open()

作为替代的构造器。 tarfile.open() 函数实际上是这个类方法的快捷方式。

TarFile.getmember(name)

返回成员 nameTarInfo 对象。 如果 name 在归档中找不到,则会引发 KeyError

注解

如果一个成员在归档中出现超过一次,它的最后一次出现会被视为是最新的版本。

TarFile.getmembers()

TarInfo 对象列表的形式返回归档的成员。 列表的顺序与归档中成员的顺序一致。

TarFile.getnames()

以名称列表的形式返回成员。 它的顺序与 getmembers() 所返回列表的顺序一致。

TarFile.list(verbose=True, **, members=None*)

将内容清单打印到 sys.stdout。 如果 verboseFalse,则将只打印成员名称。 如果为 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_ownerTrue,则将使用来自 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_ownerTrue,则将使用来自 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 对象的函数。 如果它返回 NoneTarInfo 对象将从归档中被排除。

在 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*,则它将为归档中的文件指定一个替代名称,在其他情况下,名称将从 *fileobjname 属性或 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

目标文件名的名称,该属性仅在类型为 LNKTYPESYMTYPETarInfo 对象中存在。

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 类的 encodingerrors 关键字参数控制。

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 对象,该对象将逐行遍历 csvfilecsvfile 可以是任何对象,只要这个对象支持 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*]])

dialectname 关联起来。 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 实例定义了 readerwriter 实例将具有怎样的行为。

所有可用的 Dialect 名称会由 list_dialects() 返回,并且它们可由特定的 readerwriter 类通过它们的初始化函数 (__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() 方法。创建 readerwriter 对象时,程序员可以将某个字符串或 Dialect 类的子类指定为 dialect 参数。要想补充或覆盖 dialect 参数,程序员还可以单独指定某些格式参数,这些参数的名称与下面 Dialect 类定义的属性相同。

Dialect 类支持以下属性:

Dialect.delimiter

一个用于分隔字段的单字符,默认为 ','

Dialect.doublequote

控制出现在字段中的 引号字符 本身应如何被引出。当该属性为 True 时,双写引号字符。如果该属性为 False,则在 引号字符 的前面放置 转义符。默认值为 True

在输出时,如果 doublequoteFalse,且 转义符 未指定,且在字段中发现 引号字符 时,会抛出 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)

在上面的例子里,ConfigParserinterpolation 设为 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)。 这种调用形式返回指定 sectionoption, 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'

    在用户查看时这可能会特别有问题,如果她是使用比例字体来编辑文件的话。 这就是为什么当你的应用不需要带有空行的值时,你应该考虑禁用它们。 这将使得空行每次都会作为键之间的分隔。 在上面的示例中,空行产生了两个键,keythis

  • 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/rejectenabled/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_valuedelimiters 产生冲突。

旧式 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'))     # -> "Life is hard!"

ConfigParser 对象

class configparser.ConfigParser(defaults=None, dict_type=dict, allow_no_value=False, delimiters=’=’, ‘:’, comment_prefixes=’#’, ‘;’, inline_comment_prefixes=None, strict=True, empty_lines_in_values=True, default_section=configparser.DEFAULTSECT, interpolation=BasicInterpolation(), converters={})

主配置解析器。 当给定 defaults 时,它会被初始化为包含固有默认值的字典。 当给定 dict_type 时,它将被用来创建包含节、节中的选项以及默认值的字典。

当给定 delimiters 时,它会被用作分隔键与值的子字符串的集合。 当给定 comment_prefixes 时,它将被用作在否则为空行的注释的前缀子字符串的集合。 注释可以被缩进。 当给定 inline_comment_prefixes 时,它将被用作非空行的注释的前缀子字符串的集合。

strictTrue (默认值) 时,解析器在从单个源(文件、字符串或字典)读取时将不允许任何节或选项出现重复,否则会引发 DuplicateSectionErrorDuplicateOptionError。 当 empty_lines_in_valuesFalse (默认值: True) 时,每个空行均表示一个选项的结束。 在其他情况下,一个多行选项内部的空行会被保留为值的一部分。 当 allow_no_valueTrue (默认值: False) 时,将接受没有值的选项;此种选项的值将为 None 并且它们会以不带末尾分隔符的形式被序列化。

当给定 default_section 时,它将指定存放其他节的默认值和用于插值的特殊节的名称 (通常命名为 "DEFAULT")。 该值可通过使用 default_section 实例属性在运行时被读取或修改。

插值行为可通过给出 interpolation 参数提供自定义处理程序的方式来定制。 None 可用来完全禁用插值,ExtendedInterpolation() 提供了一种更高级的变体形式,它的设计受到了 zc.buildout 的启发。

插值中使用的所有选项名称将像任何其他选项名称引用一样通过 optionxform() 方法来传递。 例如,使用 optionxform() 的默认实现(它会将选项名称转换为小写形式)时,值 foo %(bar)sfoo %(BAR)s 是等价的。

当给定 converters 时,它应当为一个字典,其中每个键代表一个类型转换器的名称而每个值则为实现从字符串到目标数据类型的转换的可调用对象。 每个转换器会获得其在解析器对象和节代理上对应的 get*() 方法。

在 3.1 版更改: 默认的 dict_typecollections.OrderedDict

在 3.2 版更改: 添加了 allow_no_value, delimiters, comment_prefixes, strict, empty_lines_in_values, default_section 以及 interpolation

在 3.5 版更改: 添加了 converters 参数。

在 3.7 版更改: defaults 参数会通过 read_dict() 来读取,提供全解析器范围内一致的行为:非字符串类型的键和值会被隐式地转换为字符串。

在 3.8 版更改: 默认的 dict_typedict,因为它现在会保留插入顺序。

  • defaults()

    返回包含实例范围内默认值的字典。

  • sections()

    返回可用节的列表;default section 不包括在该列表中。

  • add_section(section)

    向实例添加一个名为 section 的节。 如果给定名称的节已存在,将会引发 DuplicateSectionError。 如果传入了 default section 名称,则会引发 ValueError。 节名称必须为字符串;如果不是则会引发 TypeError

    在 3.2 版更改: 非字符串的节名称将引发 TypeError

  • has_section(section)

    指明相应名称的 section 是否存在于配置中。 default section 不包含在内。

  • options(section)

    返回指定 section 中可用选项的列表。

  • has_option(section, option)

    如果给定的 section 存在并且包含给定的 option 则返回 True;否则返回 False。 如果指定的 sectionNone 或空字符串,则会使用 DEFAULT。

  • read(filenames, encoding=None)

    尝试读取并解析一个包含文件名的可迭代对象,返回一个被成功解析的文件名列表。

    如果 filenames 为字符串、bytes 对象或 path-like object,它会被当作单个文件来处理。 如果 filenames 中名称对应的某个文件无法被打开,该文件将被忽略。 这样的设计使得你可以指定包含多个潜在配置文件位置的可迭代对象(例如当前目录、用户家目录以及某个系统级目录),存在于该可迭代对象中的所有配置文件都将被读取。

    如果名称对应的文件全都不存在,则 ConfigParser 实例将包含一个空数据集。 一个要求从文件加载初始值的应用应当在调用 read() 来获取任何可选文件之前使用 read_file() 来加载所要求的一个或多个文件:

    import configparser, os
    config = configparser.ConfigParser()
    config.read_file(open('defaults.cfg'))
    config.read(['site.cfg', os.path.expanduser('~/.myapp.cfg')],
                encoding='cp1250')

    3.2 新版功能: encoding 形参。 在之前的版本中,所有文件都将使用 open() 的默认编码格式来读取。

    3.6.1 新版功能: filenames 形参接受一个 path-like object。

    3.7 新版功能: filenames 形参接受一个 bytes 对象。

  • read_file(f, source=None)

    f 读取并解析配置数据,它必须是一个产生 Unicode 字符串的可迭代对象(例如以文本模式打开的文件)。

    可选参数 source 指定要读取的文件名称。 如果未给出并且 f 具有 name 属性,则该属性会被用作 source;默认值为 '<???>'

    3.2 新版功能: 替代 readfp()

  • read_string(string, source=’)

    从字符串中解析配置数据。

    可选参数 source 指定一个所传入字符串的上下文专属名称。 如果未给出,则会使用 '<string>'。 这通常应为一个文件系统路径或 URL。

    3.2 新版功能.

  • read_dict(dictionary, source=’)

    从任意一个提供了类似于字典的 items() 方法的对象加载配置。 键为节名称,值为包含节中所出现的键和值的字典。 如果所用的字典类型会保留顺序,则节和其中的键将按顺序加入。 值会被自动转换为字符串。

    可选参数 source 指定一个所传入字曲的上下文专属名称。 如果未给出,则会使用 <dict>

    此方法可被用于在解析器之间拷贝状态。

    3.2 新版功能.

  • get(section, option, **, raw=False, vars=None[, fallback*])

    获取指定名称的 section 的一个 option 的值。 如果提供了 vars*,则它必须为一个字典。 *option 的查找顺序为 vars*(如果有提供)、*section *以及 DEFAULTSECT。 如果未找到该键并且提供了 fallback,则它会被用作回退值。 可以提供 None 作为 fallback* 值。

    所有 '%' 插值会在返回值中被展开,除非 raw 参数为真值。 插值键所使用的值会按与选项相同的方式来查找。

    在 3.2 版更改: raw, varsfallback 都是仅限关键字参数,以防止用户试图使用第三个参数作业为 fallback 回退值(特别是在使用映射 协议的时候)。

  • getint(section, option, **, raw=False, vars=None[, fallback*])

    将在指定 section 中的 option 强制转换为整数的便捷方法。

  • getfloat(section, option, **, raw=False, vars=None[, fallback*])

    将在指定 section 中的 option 强制转换为浮点数的便捷方法。

  • getboolean(section, option, **, raw=False, vars=None[, fallback*])

    将在指定 section 中的 option 强制转换为布尔值的便捷方法。 请注意选项所接受的值为 '1', 'yes', 'true''on',它们会使得此方法返回 True,以及 '0', 'no', 'false''off',它们会使得此方法返回 False。 这些字符串值会以对大小写不敏感的方式被检测。 任何其他值都将导致引发 ValueError

  • items(raw=False, vars=None)

    items(section, raw=False, vars=None)

    当未给出 section 时,将返回由 section_name, section_proxy 对组成的列表,包括 DEFAULTSECT。

    在其他情况下,将返回给定的 section 中的 option 的 name, value 对组成的列表。 可选参数具有与 get() 方法的参数相同的含义。

    在 3.8 版更改: vars 中的条目将不在结果中出现。 之前的行为混淆了实际的解析器选项和为插值提供的变量。

  • set(section, option, value)

    如果给定的节存在,则将所给出的选项设为指定的值;在其他情况下将引发 NoSectionErroroptionvalue 必须为字符串;如果不是则将引发 TypeError

  • write(fileobject, space_around_delimiters=True)

    将配置的表示形式写入指定的 file object,该对象必须以文本模式打开(接受字符串)。 此表示形式可由将来的 read() 调用进行解析。 如果 space_around_delimiters 为真值,键和值之前的分隔符两边将加上空格。

注解

原始配置文件中的注释在写回配置时不会被保留。 具体哪些会被当作注释,取决于为 comment_prefixinline_comment_prefix 所指定的值。

  • remove_option(section, option)

    将指定的 option 从指定的 section 中移除。 如果指定的节不存在则会引发 NoSectionError。 如果要移除的选项存在则返回 True;在其他情况下将返回 False

  • remove_section(section)

    从配置中移除指定的 section。 如果指定的节确实存在则返回 True。 在其他情况下将返回 False

  • optionxform(option)

    将选项名 option 转换为输入文件中的形式或客户端代码所传入的应当在内部结构中使用的形式。 默认实现将返回 option 的小写形式版本;子类可以重载此行为,或者客户端代码也可以在实例上设置一个具有此名称的属性来影响此行为。

    你不需要子类化解析器来使用此方法,你也可以在一个实例上设置它,或使用一个接受字符串参数并返回字符串的函数。 例如将它设为 str 将使得选项名称变得大小写敏感:

    cfgparser = ConfigParser()
    cfgparser.optionxform = str

    请注意当读取配置文件时,选项名称两边的空格将在调用 optionxform() 之前被去除。

  • readfp(fp, filename=None)

    3.2 版后已移除: 使用 read_file() 来代替。

    在 3.2 版更改: readfp() 现在将在 fp 上执行迭代而不是调用 fp.readline()

    对于调用 readfp() 时传入不支持迭代的参数的现有代码,可以在文件类对象外使用以下生成器作为包装器:

    def readline_generator(fp):
        line = fp.readline()
        while line:
            yield line
            line = fp.readline()

    不再使用 parser.readfp(fp) 而是改用 parser.read_file(readline_generator(fp))

configparser.MAX_INTERPOLATION_DEPTH

raw 形参为假值时 get() 所采用的递归插值的最大深度。 这只在使用默认的 interpolation 时会起作用。

RawConfigParser 对象

class configparser.RawConfigParser(defaults=None, dict_type=dict, allow_no_value=False, **, delimiters=(‘=’, ‘:’), comment_prefixes=(‘#’, ‘;’), inline_comment_prefixes=None, strict=True, empty_lines_in_values=True, default_section=configparser.DEFAULTSECT[, interpolation*])

旧式 ConfigParser。 它默认禁用插值并且允许通过不安全的 add_sectionset 方法以及旧式 defaults= 关键字参数处理来设置非字符串的节名、选项名和值。

在 3.8 版更改: 默认的 dict_typedict,因为它现在会保留插入顺序。

注解

考虑改用 ConfigParser,它会检查内部保存的值的类型。 如果你不想要插值,你可以使用 ConfigParser(interpolation=None)

  • add_section(section)

    向实例添加一个名为 section 的节。 如果给定名称的节已存在,将会引发 DuplicateSectionError。 如果传入了 default section 名称,则会引发 ValueError

    不检查 section 以允许用户创建以非字符串命名的节。 此行为已不受支持并可能导致内部错误。

  • set(section, option, value)

    如果给定的节存在,则将给定的选项设为指定的值;在其他情况下将引发 NoSectionError。 虽然可能使用 RawConfigParser (或使用 ConfigParser 并将 raw 形参设为真值) 以便实现非字符串值的 internal 存储,但是完整功能(包括插值和输出到文件)只能使用字符串值来实现。

    此方法允许用户在内部将非字符串值赋给键。 此行为已不受支持并会在尝试写入到文件或在非原始模式下获取数据时导致错误。 请使用映射协议 API,它不允许出现这样的赋值。

异常

exception configparser.Error

所有其他 configparser 异常的基类。

exception configparser.NoSectionError

当找不到指定节时引发的异常。

exception configparser.DuplicateSectionError

当调用 add_section() 时传入已存在的节名称,或者在严格解析器中当单个输入文件、字符串或字典内出现重复的节时引发的异常。

3.2 新版功能: 将可选的 sourcelineno 属性和参数添加到 __init__()

exception configparser.DuplicateOptionError

当单个选项在从单个文件、字符串或字典读取时出现两次时引发的异常。 这会捕获拼写错误和大小写敏感相关的错误,例如一个字典可能包含两个键分别代表同一个大小写不敏感的配置键。

exception configparser.NoOptionError

当指定的选项未在指定的节中被找到时引发的异常。

exception configparser.InterpolationError

当执行字符串插值发生问题时所引发的异常的基类。

exception configparser.InterpolationDepthError

当字符串插值由于迭代次数超出 MAX_INTERPOLATION_DEPTH 而无法完成所引发的异常。 为 InterpolationError 的子类。

exception configparser.InterpolationMissingOptionError

当从某个值引用的选项并不存在时引发的异常。 为 InterpolationError 的子类。

exception configparser.InterpolationSyntaxError

当将要执行替换的源文本不符合要求的语法时引发的异常。 为 InterpolationError 的子类。

exception configparser.MissingSectionHeaderError

当尝试解析一个不带节标头的文件时引发的异常。

exception configparser.ParsingError

当尝试解析一个文件而发生错误时引发的异常。

在 3.2 版更改: filename 属性和 __init__() 参数被重命名为 source 以保持一致性。

netrc —- netrc 文件处理

源代码: Lib/netrc.py


netrc 类解析并封装了 Unix 的 ftp 程序和其他 FTP 客户端所使用的 netrc 文件格式。

class netrc.netrc([file])

netrc 的实例或其子类的实例会被用来封装来自 netrc 文件的数据。 如果有初始化参数,它将指明要解析的文件。 如果未给出参数,则位于用户家目录的 .netrc 文件 — 即 os.path.expanduser() 所确定的文件 — 将会被读取。 在其他情况下,则将引发 FileNotFoundError 异常。 解析错误将引发 NetrcParseError 并附带诊断信息,包括文件名、行号以及终止令牌。 如果在 POSIX 系统上未指明参数,则当 .netrc 文件中有密码时,如果文件归属或权限不安全(归属的用户不是运行进程的用户,或者可供任何其他用户读取或写入)将引发 NetrcParseError。 这实现了与 ftp 和其他使用 .netrc 的程序同等的安全行为。

在 3.4 版更改: 添加了 POSIX 权限检查。

在 3.7 版更改: 当未将 file 作为参数传入时会使用 os.path.expanduser() 来查找 .netrc 文件的位置。

在 3.10 版更改: netrc 会在使用语言区域指定的编码格式之前先尝试 UTF-8 编码格式。

exception netrc.NetrcParseError

当在源文本中遇到语法错误时由 netrc 类引发的异常。 此异常的实例提供了三个有用属性: msg 为错误的文本说明,filename 为源文件的名称,而 lineno 给出了错误所在的行号。

netrc 对象

netrc 实例具有下列方法:

netrc.authenticators(host)

针对 host 的身份验证者返回一个 3 元组 (login, account, password)。 如果 netrc 文件不包含针对给定主机的条目,则返回关联到 ‘default’ 条目的元组。 如果匹配的主机或默认条目均不可用,则返回 None

netrc.__repr__()

将类数据以 netrc 文件的格式转储为一个字符串。 (这会丢弃注释并可能重排条目顺序。)

netrc 的实例具有一些公共实例变量:

netrc.hosts

将主机名映射到 (login, account, password) 元组的字典。 如果存在 ‘default’ 条目,则会表示为使用该名称的伪主机。

netrc.macros

将宏名称映射到字符串列表的字典。

注解

密码会被限制为 ASCII 字符集的一个子集。 所有 ASCII 标点符号均可用作密码,但是要注意空白符和非打印字符不允许用作密码。 这是 .netrc 文件解析方式带来的限制,在未来可能会被解除。

xdrlib —- 编码与解码 XDR 数据

源代码: Lib/xdrlib.py


xdrlib 模块为外部数据表示标准提供支持,该标准的描述见 RFC 1014,由 Sun Microsystems, Inc. 在 1987 年 6 月撰写。 它支持该 RFC 中描述的大部分数据类型。

xdrlib 模块定义了两个类,一个用于将变量打包为 XDR 表示形式,另一个用于从 XDR 表示形式解包。 此外还有两个异常类。

class xdrlib.Packer

Packer 是用于将数据打包为 XDR 表示形式的类。 Packer 类的实例化不附带参数。

class xdrlib.Unpacker(data)

Unpacker 是用于相应地从字符串缓冲区解包 XDR 数据值的类。 输入缓冲区将作为 data 给出。

参见

RFC 1014 - XDR: 外部数据表示标准

这个 RFC 定义了最初编写此模块时 XDR 所用的数据编码格式。 显然它已被 RFC 1832 所淘汰。

RFC 1832 - XDR: 外部数据表示标准

更新的 RFC,它提供了经修订的 XDR 定义。

Packer 对象

Packer 实例具有下列方法:

Packer.get_buffer()

将当前打包缓冲区以字符串的形式返回。

Packer.reset()

将打包缓冲区重置为空字符串。

总体来说,你可以通过调用适当的 pack_type() 方法来打包任何最常见的 XDR 数据类型。 每个方法都是接受单个参数,即要打包的值。 受支持的简单数据类型打包方法如下: pack_uint(), pack_int(), pack_enum(), pack_bool(), pack_uhyper() 以及 pack_hyper()

Packer.pack_float(value)

打包单精度浮点数 value

Packer.pack_double(value)

打包双精度浮点数 value

以下方法支持打包字符串、字节串以及不透明数据。

Packer.pack_fstring(n, s)

打包固定长度字符串 sn 为字符串的长度,但它 不会 被打包进数据缓冲区。 如有必要字符串会以空字节串填充以保证 4 字节对齐。

Packer.pack_fopaque(n, data)

打包固定长度不透明数据流,类似于 pack_fstring()

Packer.pack_string(s)

打包可变长度字符串 s。 先将字符串的长度打包为无符号整数,再用 pack_fstring() 来打包字符串数据。

Packer.pack_opaque(data)

打包可变长度不透明数据流,类似于 pack_string()

Packer.pack_bytes(bytes)

打包可变长度字节流,类似于 pack_string()

下列方法支持打包数组和列表:

Packer.pack_list(list, pack_item)

打包由同质条目构成的 list*。 此方法适用于不确定长度的列表;即其长度无法在遍历整个列表之前获知。 对于列表中的每个条目,先打包一个无符号整数 1,再添加列表中数据的值。 *pack_item 是在打包单个条目时要调用的函数。 在列表的末尾,会再打包一个无符号整数 0

例如,要打包一个整数列表,代码看起来会是这样:

import xdrlib
p = xdrlib.Packer()
p.pack_list([1, 2, 3], p.pack_int)

Packer.pack_farray(n, array, pack_item)

打包由同质条目构成的固定长度列表 (array)。 n 为列表长度;它 不会 被打包到缓冲区,但是如果 len(array) 不等于 n 则会引发 ValueError。 如上所述,pack_item 是在打包每个元素时要使用的函数。

Packer.pack_array(list, pack_item)

打包由同质条目构成的可变长度 list。 先将列表的长度打包为无符号整数,再像上面的 pack_farray() 一样打包每个元素。

Unpacker 对象

Unpacker 类提供以下方法:

Unpacker.reset(data)

使用给定的 data 重置字符串缓冲区。

Unpacker.get_position()

返回数据缓冲区中的当前解包位置。

Unpacker.set_position(position)

将数据缓冲区的解包位置设为 position。 你应当小心使用 get_position()set_position()

Unpacker.get_buffer()

将当前解包数据缓冲区以字符串的形式返回。

Unpacker.done()

表明解包完成。 如果数据没有全部完成解包则会引发 Error 异常。

此外,每种可通过 Packer 打包的数据类型都可通过 Unpacker 来解包。 解包方法的形式为 unpack_type(),并且不接受任何参数。 该方法将返回解包后的对象。

Unpacker.unpack_float()

解包单精度浮点数。

Unpacker.unpack_double()

解包双精度浮点数,类似于 unpack_float()

此外,以下方法可用来解包字符串、字节串以及不透明数据:

Unpacker.unpack_fstring(n)

解包并返回固定长度字符串。 n 为期望的字符数量。 会预设以空字节串填充以保证 4 字节对齐。

Unpacker.unpack_fopaque(n)

解包并返回固定长度数据流,类似于 unpack_fstring()

Unpacker.unpack_string()

解包并返回可变长度字符串。 先将字符串的长度解包为无符号整数,再用 unpack_fstring() 来解包字符串数据。

Unpacker.unpack_opaque()

解包并返回可变长度不透明数据流,类似于 unpack_string()

Unpacker.unpack_bytes()

解包并返回可变长度字节流,类似于 unpack_string()

下列方法支持解包数组和列表:

Unpacker.unpack_list(unpack_item)

解包并返回同质条目的列表。 该列表每次解包一个元素,先解包一个无符号整数旗标。 如果旗标为 1,则解包条目并将其添加到列表。 旗标为 0 表明列表结束。 unpack_item 为在解包条目时调用的函数。

Unpacker.unpack_farray(n, unpack_item)

解包并(以列表形式)返回由同质条目构成的固定长度数组。 n 为期望的缓冲区内列表元素数量。 如上所述,unpack_item 是解包每个元素时要使用的函数。

Unpacker.unpack_array(unpack_item)

解包并返回由同质条目构成的可变长度 list。 先将列表的长度解包为无符号整数,再像上面的 unpack_farray() 一样解包每个元素。

异常

此模块中的异常会表示为类实例代码:

exception xdrlib.Error

基本异常类。 Error 具有一个公共属性 msg,其中包含对错误的描述。

exception xdrlib.ConversionError

Error 所派生的类。 不包含额外的实例变量。

以下是一个应该如何捕获这些异常的示例:

import xdrlib
p = xdrlib.Packer()
try:
    p.pack_double(8.01)
except xdrlib.ConversionError as instance:
    print('packing the double failed:', instance.msg)

plistlib —- 生成与解析 Apple .plist 文件

源代码: Lib/plistlib.py


此模块提供了可读写 Apple “property list” 文件的接口,它主要用于 macOS 和 iOS 系统。 此模块同时支持二进制和 XML plist 文件。

property list (.plist) 文件格式是一种简单的序列化格式,它支持一些基本对象类型,例如字典、列表、数字和字符串等。 通常使用一个字典作为最高层级对象。

要写入和解析 plist 文件,请使用 dump()load() 函数。

要以字节串对象形式操作 plist 数据,请使用 dumps()loads()

值可以为字符串、整数、浮点数、布尔值、元组、列表、字典(但只允许用字符串作为键)、bytesbytearraydatetime.datetime 对象。

在 3.4 版更改: 新版 API,旧版 API 已被弃用。 添加了对二进制 plist 格式的支持。

在 3.8 版更改: 添加了在二进制 plist 中读写 UID 令牌的支持,例如用于 NSKeyedArchiver 和 NSKeyedUnarchiver。

在 3.9 版更改: 旧 API 已被移除。

这个模块定义了以下函数:

plistlib.load(fp, **, fmt=None, dict_type=dict*)

读取 plist 文件。 fp 应当可读并且为二进制文件对象。 返回已解包的根对象(通常是一个字典)。

fmt 为文件的格式,有效的值如下:

  • None: 自动检测文件格式
  • FMT_XML: XML 文件格式
  • FMT_BINARY: 二进制 plist 格式

dict_type 为字典用来从 plist 文件读取的类型。

FMT_XML 格式的 XML 数据 会使用来自 xml.parsers.expat 的 Expat 解析器 — 请参阅其文档了解错误格式 XML 可能引发的异常。 未知元素将被 plist 解析器直接略过。

当文件无法被解析时二进制格式的解析器将引发 InvalidFileException

3.4 新版功能.

plistlib.loads(data, **, fmt=None, dict_type=dict*)

3.4 新版功能.

plistlib.dump(value, fp, **, fmt=FMT_XML, sort_keys=True, skipkeys=False*)

value 写入 plist 文件。 Fp 应当可写并且为二进制文件对象。

fmt 参数指定 plist 文件的格式,可以是以下值之一:

  • FMT_XML: XML 格式的 plist 文件
  • FMT_BINARY: 二进制格式的 plist 文件

sort_keys 为真值(默认)时字典的键将经过排序再写入 plist,否则将按字典的迭代顺序写入。

skipkeys 为假值(默认)时该函数将在字典的键不为字符串时引发 TypeError,否则将跳过这样的键。

如果对象是不受支持的类型或者是包含不受支持类型的对象的容器则将引发 TypeError

对于无法在(二进制)plist 文件中表示的整数值,将会引发 OverflowError

3.4 新版功能.

plistlib.dumps(value, **, fmt=FMT_XML, sort_keys=True, skipkeys=False*)

value 以 plist 格式字节串对象的形式返回。

3.4 新版功能.

可以使用以下的类:

class plistlib.UID(data)

包装一个 int。 该类将在读取或写入 NSKeyedArchiver 编码的数据时被使用,其中包含 UID(参见 PList 指南)。

It has one attribute, data, which can be used to retrieve the int value of the UID. data must be in the range 0 <= data < 2**64.

3.8 新版功能.

可以使用以下的常量:

plistlib.FMT_XML

用于 plist 文件的 XML 格式。

3.4 新版功能.

plistlib.FMT_BINARY

用于 plist 文件的二进制格式。

3.4 新版功能.

例子

生成一个 plist:

pl = dict(
    aString = "Doodah",
    aList = ["A", "B", 12, 32.1, [1, 2, 3]],
    aFloat = 0.1,
    anInt = 728,
    aDict = dict(
        anotherString = "<hello & hi there!>",
        aThirdString = "M\xe4ssig, Ma\xdf",
        aTrueValue = True,
        aFalseValue = False,
    ),
    someData = b"<binary gunk>",
    someMoreData = b"<lots of binary gunk>" * 10,
    aDate = datetime.datetime.fromtimestamp(time.mktime(time.gmtime())),
)
with open(fileName, 'wb') as fp:
    dump(pl, fp)

解析一个 plist:

with open(fileName, 'rb') as fp:
    pl = load(fp)
print(pl["aKey"])

加密服务

  • hashlib —- 安全哈希与消息摘要
    • 哈希算法
    • SHAKE 可变长度摘要
    • 密钥派生
    • BLAKE2
      • 创建哈希对象
      • 常量
      • 例子
        • 简单哈希
        • 使用不同的摘要大小
        • 密钥哈希
        • 随机哈希
        • 个性化
        • 树形模式
      • 开发人员
  • hmac —- 基于密钥的消息验证
  • secrets —- 生成管理密码的安全随机数
    • 随机数
    • 生成 Token
      • Token 应当使用多少个字节?
    • 其他功能
    • 应用技巧与最佳实践

hashlib —- 安全哈希与消息摘要

源码: Lib/hashlib.py


这个模块针对许多不同的安全哈希和消息摘要算法实现了一个通用接口。 包括 FIPS 安全哈希算法 SHA1, SHA224, SHA256, SHA384 和 SHA512 (定义于 FIPS 180-2) 以及 RSA 的 MD5 算法 (定义于互联网 RFC 1321)。 术语 “安全哈希” 和 “消息摘要” 是同义的。 较旧的算法被称为消息摘要。 现代的术语是安全哈希。

注解

如果你想找到 adler32 或 crc32 哈希函数,它们在 zlib 模块中。

警告

有些算法已知存在哈希碰撞弱点,请参考最后的“另请参阅”段。

哈希算法

每种类型的 hash 都有一个构造器方法。 它们都返回一个具有相同的简单接口的 hash 对象。 例如,使用 use sha256() 创建一个 SHA-256 hash 对象。 你可以使用 update() 方法向这个对象输入 字节类对象 (通常是 bytes)。 在任何时候你都可以使用 digest()hexdigest() 方法获得到目前为止输入这个对象的拼接数据的 digest

注解

为了更好的多线程性能,在对象创建或者更新时,若数据大于2047字节则 Python 的 GIL 会被释放。

注解

update() 输入字符串对象是不被支持的,因为哈希基于字节而非字符。

此模块中总是可用的哈希算法构造器有 sha1(), sha224(), sha256(), sha384(), sha512(), blake2b()blake2s()md5() 通常也是可用的,但如果你在使用少见的 “FIPS 兼容” 的 Python 编译版本则可能会找不到它。 此外还可能有一些附加的算法,具体取决于你的平台上的 Python 所使用的 OpenSSL 库。 在大部分平台上可用的还有 sha3_224(), sha3_256(), sha3_384(), sha3_512(), shake_128(), shake_256() 等等。·

3.6 新版功能: SHA3 (Keccak) 和 SHAKE 构造器 sha3_224(), sha3_256(), sha3_384(), sha3_512(), shake_128(), shake_256().

3.6 新版功能: 添加了 blake2b()blake2s()

在 3.9 版更改: 所有 hashlib 的构造器都接受仅限关键字参数 usedforsecurity 且其默认值为 True。 设为假值即允许在受限的环境中使用不安全且阻塞的哈希算法。 False 表示此哈希算法不可用于安全场景,例如用作非加密的单向压缩函数。

现在 hashlib 会使用 OpenSSL 1.1.1 或更新版本的 SHA3 和 SHAKE。

例如,如果想获取字节串 b'Nobody inspects the spammish repetition' 的摘要:

>>> import hashlib
>>> m = hashlib.sha256()
>>> m.update(b"Nobody inspects")
>>> m.update(b" the spammish repetition")
>>> m.digest()
b'\x03\x1e\xdd}Ae\x15\x93\xc5\xfe\\\x00o\xa5u+7\xfd\xdf\xf7\xbcN\x84:\xa6\xaf\x0c\x95\x0fK\x94\x06'
>>> m.digest_size
32
>>> m.block_size
64

更简要的写法:

>>> hashlib.sha224(b"Nobody inspects the spammish repetition").hexdigest()
'a4337bc45a8fc544c03f52dc550cd6e1e87021bc896588bd79e901e2'

hashlib.new(name, [data, ]**, usedforsecurity=True*)

一个接受所希望的算法对应的字符串 name 作为第一个形参的通用构造器。 它还允许访问上面列出的哈希算法以及你的 OpenSSL 库可能提供的任何其他算法。 同名的构造器要比 new() 更快所以应当优先使用。

使用 new() 并附带由 OpenSSL 所提供了算法:

>>> h = hashlib.new('sha512_256')
>>> h.update(b"Nobody inspects the spammish repetition")
>>> h.hexdigest()
'19197dc4d03829df858011c6c87600f994a858103bbc19005f20987aa19a97e2'

Hashlib 提供下列常量属性:

hashlib.algorithms_guaranteed

一个集合,其中包含此模块在所有平台上都保证支持的哈希算法的名称。 请注意 ‘md5’ 也在此清单中,虽然某些上游厂商提供了一个怪异的排除了此算法的 “FIPS 兼容” Python 编译版本。

3.2 新版功能.

hashlib.algorithms_available

一个集合,其中包含在所运行的 Python 解释器上可用的哈希算法的名称。 将这些名称传给 new() 时将可被识别。 algorithms_guaranteed 将总是它的一个子集。 同样的算法在此集合中可能以不同的名称出现多次(这是 OpenSSL 的原因)。

3.2 新版功能.

下列值会以构造器所返回的哈希对象的常量属性的形式被提供:

hash.digest_size

以字节表示的结果哈希对象的大小。

hash.block_size

以字节表示的哈希算法的内部块大小。

hash 对象具有以下属性:

hash.name

此哈希对象的规范名称,总是为小写形式并且总是可以作为 new() 的形参用来创建另一个此类型的哈希对象。

在 3.4 版更改: 该属性名称自被引入起即存在于 CPython 中,但在 Python 3.4 之前并未正式指明,因此可能不存在于某些平台上。

哈希对象具有下列方法:

hash.update(data)

用 bytes-like object 来更新哈希对象。 重复调用相当于单次调用并传入所有参数的拼接结果: m.update(a); m.update(b) 等价于 m.update(a+b)

在 3.1 版更改: 当使用 OpenSSL 提供的哈希算法在大于 2047 字节的数据上执行哈希更新时 Python GIL 会被释放以允许其他线程运行。

hash.digest()

返回当前已传给 update() 方法的数据摘要。 这是一个大小为 digest_size 的字节串对象,字节串中可包含 0 至 255 的完整取值范围。

hash.hexdigest()

类似于 digest() 但摘要会以两倍长度字符串对象的形式返回,其中仅包含十六进制数码。 这可以被用于在电子邮件或其他非二进制环境中安全地交换数据值。

hash.copy()

返回哈希对象的副本(“克隆”)。 这可被用来高效地计算共享相同初始子串的数据的摘要。

SHAKE 可变长度摘要

shake_128()shake_256() 算法提供安全的 length_in_bits//2 至 128 或 256 位可变长度摘要。 为此,它们的摘要需指定一个长度。 SHAKE 算法不限制最大长度。

shake.digest(length)

返回当前已传给 update() 方法的数据摘要。 这是一个大小为 length 的字节串对象,字节串中可包含 0 to 255 的完整取值范围。

shake.hexdigest(length)

类似于 digest() 但摘要会以两倍长度字符串对象的形式返回,其中仅包含十六进制数码。 这可以被用于在电子邮件或其他非二进制环境中安全地交换数据值。

密钥派生

密钥派生和密钥延展算法被设计用于安全密码哈希。 sha1(password) 这样的简单算法无法防御暴力攻击。 好的密码哈希函数必须可以微调、放慢步调,并且包含 加盐

hashlib.pbkdf2_hmac(hash_name, password, salt, iterations, dklen=None)

此函数提供 PKCS#5 基于密码的密钥派生函数 2。 它使用 HMAC 作为伪随机函数。

字符串 hash_name 是要求用于 HMAC 的哈希摘要算法的名称,例如 ‘sha1’ 或 ‘sha256’。 passwordsalt 会以字节串缓冲区的形式被解析。 应用和库应当将 password 限制在合理长度 (例如 1024)。 salt 应当为适当来源例如 os.urandom() 的大约 16 个或更多的字节串数据。

iterations 数值应当基于哈希算法和算力来选择。 在 2013 年时,建议至少为 100,000 次 SHA-256 迭代。

dklen 为派生密钥的长度。 如果 dklenNone 则会使用哈希算法 hash_name 的摘要大小,例如 SHA-512 为 64。

>>> import hashlib
>>> dk = hashlib.pbkdf2_hmac('sha256', b'password', b'salt', 100000)
>>> dk.hex()
'0394a2ede332c9a13eb82e9b24631604c31df978b4e2f0fbd2c549944f9d79a5'

3.4 新版功能.

注解

随同 OpenSSL 提供了一个快速的 pbkdf2_hmac 实现。 Python 实现是使用 hmac 的内联版本。 它的速度大约要慢上三倍并且不会释放 GIL。

3.10 版后已移除: 较慢的 pbkdf2_hmac Python 实现已被弃用。 未来该函数将仅在 Python 附带 OpenSSL 编译时可用。

hashlib.scrypt(password, **, salt, n, r, p, maxmem=0, dklen=64*)

此函数提供基于密码加密的密钥派生函数,其定义参见 RFC 7914

passwordsalt 必须为 字节类对象。 应用和库应当将 password 限制在合理长度 (例如 1024)。 salt 应当为适当来源例如 os.urandom() 的大约 16 个或更多的字节串数据。

n 为 CPU/内存开销因子,r 为块大小,p 为并行化因子,maxmem 为内存限制 (OpenSSL 1.1.0 默认为 32 MiB)。 dklen 为派生密钥的长度。

3.6 新版功能.

BLAKE2

BLAKE2 是在 RFC 7693 中定义的加密哈希函数,它有两种形式:

  • BLAKE2b,针对 64 位平台进行优化,并会生成长度介于 1 和 64 字节之间任意大小的摘要。
  • BLAKE2s,针对 8 至 32 位平台进行优化,并会生成长度介于 1 和 32 字节之间任意大小的摘要。

BLAKE2 支持 keyed mode (HMAC 的更快速更简单的替代), salted hashing, personalizationtree hashing.

此模块的哈希对象遵循标准库 hashlib 对象的 API。

创建哈希对象

新哈希对象可通过调用构造器函数来创建:

hashlib.blake2b(data=b’’, **, digest_size=64, key=b’’, salt=b’’, person=b’’, fanout=1, depth=1, leaf_size=0, node_offset=0, node_depth=0, inner_size=0, last_node=False, usedforsecurity=True*)

hashlib.blake2s(data=b’’, **, digest_size=32, key=b’’, salt=b’’, person=b’’, fanout=1, depth=1, leaf_size=0, node_offset=0, node_depth=0, inner_size=0, last_node=False, usedforsecurity=True*)

这些函数返回用于计算 BLAKE2b 或 BLAKE2s 的相应的哈希对象。 它们接受下列可选通用形参:

  • data: 要哈希的初始数据块,它必须为 bytes-like object。 它只能作为位置参数传入。
  • digest_size: 以字节数表示的输出摘要大小。
  • key: 用于密钥哈希的密钥(对于 BLAKE2b 最长 64 字节,对于 BLAKE2s 最长 32 字节)。
  • salt: 用于随机哈希的盐值(对于 BLAKE2b 最长 16 字节,对于 BLAKE2s 最长 8 字节)。
  • person: 个性化字符串(对于 BLAKE2b 最长 16 字节,对于 BLAKE2s 最长 8 字节)。

下表显示了常规参数的限制(以字节为单位):

Hash 目标长度 长度(键) 长度(盐) 长度(个人)
BLAKE2b 64 64 16 16
BLAKE2s 32 32 8 8

注解

BLAKE2 规格描述为盐值和个性化形参定义了固定的长度,但是为了方便起见,此实现接受指定在长度以内的任意大小的字节串。 如果形参长度小于指定值,它将以零值进行填充,因此举例来说,b'salt'b'salt\x00' 为相同的值 (key 的情况则并非如此。)

如下面的模块 constants 所描述,这些是可用的大小取值。

构造器函数还接受下列树形哈希形参:

  • fanout: 扇出值 (0 至 255,如无限制即为 0,连续模式下为 1)。
  • depth: 树的最大深度 (1 至 255,如无限制则为 255,连续模式下为 1)。
  • leaf_size: maximal byte length of leaf (0 to 2**32-1, 0 if unlimited or in sequential mode).
  • node_offset: node offset (0 to 2**64-1 for BLAKE2b, 0 to 2**48-1 for BLAKE2s, 0 for the first, leftmost, leaf, or in sequential mode).
  • node_depth: 节点深度 (0 至 255,对于叶子或在连续模式下则为 0)。
  • inner_size: 内部摘要大小 (对于 BLAKE2b 为 0 至 64,对于 BLAKE2s 为 0 至 32,连续模式下则为 0)。
  • last_node: 一个布尔值,指明所处理的节点是否为最后一个 (连续模式下则为 False)。

请参阅 BLAKE2 规格描述 第 2.10 节获取有关树形哈希的完整说明。

常量

blake2b.SALT_SIZE
blake2s.SALT_SIZE

盐值长度(构造器所接受的最大长度)。

blake2b.PERSON_SIZE
blake2s.PERSON_SIZE

个性化字符串长度(构造器所接受的最大长度)。

blake2b.MAX_KEY_SIZE
blake2s.MAX_KEY_SIZE

最大密钥长度。

blake2b.MAX_DIGEST_SIZE
blake2s.MAX_DIGEST_SIZE

哈希函数可输出的最大摘要长度。

例子

简单哈希

要计算某个数据的哈希值,你应该首先通过调用适当的构造器函数 (blake2b()blake2s()) 来构造一个哈希对象,然后通过在该对象上调用 update() 来更新目标数据,最后通过调用 digest() (或针对十六进制编码字符串的 hexdigest()) 来获取该对象的摘要。

>>> from hashlib import blake2b
>>> h = blake2b()
>>> h.update(b'Hello world')
>>> h.hexdigest()
'6ff843ba685842aa82031d3f53c48b66326df7639a63d128974c5c14f31a0f33343a8c65551134ed1ae0f2b0dd2bb495dc81039e3eeb0aa1bb0388bbeac29183'

作为快捷方式,你可以直接以位置参数的形式向构造器传入第一个数据块来直接更新:

>>> from hashlib import blake2b
>>> blake2b(b'Hello world').hexdigest()
'6ff843ba685842aa82031d3f53c48b66326df7639a63d128974c5c14f31a0f33343a8c65551134ed1ae0f2b0dd2bb495dc81039e3eeb0aa1bb0388bbeac29183'

你可以多次调用 hash.update() 至你所想要的任意次数以迭代地更新哈希值:

>>> from hashlib import blake2b
>>> items = [b'Hello', b' ', b'world']
>>> h = blake2b()
>>> for item in items:
...     h.update(item)
>>> h.hexdigest()
'6ff843ba685842aa82031d3f53c48b66326df7639a63d128974c5c14f31a0f33343a8c65551134ed1ae0f2b0dd2bb495dc81039e3eeb0aa1bb0388bbeac29183'
使用不同的摘要大小

BLAKE2 具有可配置的摘要大小,对于 BLAKE2b 最多 64 字节,对于 BLAKE2s 最多 32 字节。 例如,要使用 BLAKE2b 来替代 SHA-1 而不改变输出大小,我们可以让 BLAKE2b 产生 20 个字节的摘要:

>>> from hashlib import blake2b
>>> h = blake2b(digest_size=20)
>>> h.update(b'Replacing SHA1 with the more secure function')
>>> h.hexdigest()
'd24f26cf8de66472d58d4e1b1774b4c9158b1f4c'
>>> h.digest_size
20
>>> len(h.digest())
20

不同摘要大小的哈希对象具有完全不同的输出(较短哈希值 并非 较长哈希值的前缀);即使输出长度相同,BLAKE2b 和 BLAKE2s 也会产生不同的输出:

>>> from hashlib import blake2b, blake2s
>>> blake2b(digest_size=10).hexdigest()
'6fa1d8fcfd719046d762'
>>> blake2b(digest_size=11).hexdigest()
'eb6ec15daf9546254f0809'
>>> blake2s(digest_size=10).hexdigest()
'1bf21a98c78a1c376ae9'
>>> blake2s(digest_size=11).hexdigest()
'567004bf96e4a25773ebf4'
密钥哈希

密钥哈希可被用于身份验证,作为 基于哈希的消息验证代码 (HMAC) 的一种更快速更简单的替代。 BLAKE2 可在前缀 MAC 模式下安全地使用,这是由于它从 BLAKE 所继承的不可区分特性。

这个例子演示了如何使用密钥 b'pseudorandom key' 来为 b'message data' 获取一个(十六进制编码的)128 位验证代码:

>>> from hashlib import blake2b
>>> h = blake2b(key=b'pseudorandom key', digest_size=16)
>>> h.update(b'message data')
>>> h.hexdigest()
'3d363ff7401e02026f4a4687d4863ced'

作为实际的例子,一个 Web 应用可为发送给用户的 cookies 进行对称签名,并在之后对其进行验证以确保它们没有被篡改:

>>> from hashlib import blake2b
>>> from hmac import compare_digest
>>>
>>> SECRET_KEY = b'pseudorandomly generated server secret key'
>>> AUTH_SIZE = 16
>>>
>>> def sign(cookie):
...     h = blake2b(digest_size=AUTH_SIZE, key=SECRET_KEY)
...     h.update(cookie)
...     return h.hexdigest().encode('utf-8')
>>>
>>> def verify(cookie, sig):
...     good_sig = sign(cookie)
...     return compare_digest(good_sig, sig)
>>>
>>> cookie = b'user-alice'
>>> sig = sign(cookie)
>>> print("{0},{1}".format(cookie.decode('utf-8'), sig))
user-alice,b'43b3c982cf697e0c5ab22172d1ca7421'
>>> verify(cookie, sig)
True
>>> verify(b'user-bob', sig)
False
>>> verify(cookie, b'0102030405060708090a0b0c0d0e0f00')
False

即使存在原生的密钥哈希模式,BLAKE2 也同样可在 hmac 模块的 HMAC 构造过程中使用:

>>> import hmac, hashlib
>>> m = hmac.new(b'secret key', digestmod=hashlib.blake2s)
>>> m.update(b'message')
>>> m.hexdigest()
'e3c8102868d28b5ff85fc35dda07329970d1a01e273c37481326fe0c861c8142'
随机哈希

用户可通过设置 salt 形参来为哈希函数引入随机化。 随机哈希适用于防止对数字签名中使用的哈希函数进行碰撞攻击。

随机哈希被设计用来处理当一方(消息准备者)要生成由另一方(消息签名者)进行签名的全部或部分消息的情况。 如果消息准备者能够找到加密哈希函数的碰撞现象(即两条消息产生相同的哈希值),则他们就可以准备将产生相同哈希值和数字签名但却具有不同结果的有意义的消息版本(例如向某个账户转入 $1,000,000 而不是 $10)。 加密哈希函数的设计都是以防碰撞性能为其主要目标之一的,但是当前针对加密哈希函数的集中攻击可能导致特定加密哈希函数所提供的防碰撞性能低于预期。 随机哈希为签名者提供了额外的保护,可以降低准备者在数字签名生成过程中使得两条或更多条消息最终产生相同哈希值的可能性 —- 即使为特定哈希函数找到碰撞现象是可行的。 但是,当消息的所有部分均由签名者准备时,使用随机哈希可能降低数字签名所提供的安全性。

(NIST SP-800-106 “数字签名的随机哈希”)

在 BLAKE2 中,盐值会在初始化期间作为对哈希函数的一次性输入而不是对每个压缩函数的输入来处理。

警告

使用 BLAKE2 或任何其他通用加密哈希函数例如 SHA-256 进行 加盐哈希 (或纯哈希) 并不适用于哈希密码。 请参阅 BLAKE2 FAQ 了解更多信息。

>>> import os
>>> from hashlib import blake2b
>>> msg = b'some message'
>>> # Calculate the first hash with a random salt.
>>> salt1 = os.urandom(blake2b.SALT_SIZE)
>>> h1 = blake2b(salt=salt1)
>>> h1.update(msg)
>>> # Calculate the second hash with a different random salt.
>>> salt2 = os.urandom(blake2b.SALT_SIZE)
>>> h2 = blake2b(salt=salt2)
>>> h2.update(msg)
>>> # The digests are different.
>>> h1.digest() != h2.digest()
True
个性化

出于不同的目的强制让哈希函数为相同的输入生成不同的摘要有时也是有用的。 正如 Skein 哈希函数的作者所言:

我们建议所有应用设计者慎重考虑这种做法;我们已看到有许多协议在协议的某一部分中计算出来的哈希值在另一个完全不同的部分中也可以被使用,因为两次哈希计算是针对类似或相关的数据进行的,这样攻击者可以强制应用为相同的输入生成哈希值。 个性化协议中所使用的每个哈希函数将有效地阻止这种类型的攻击。

(Skein 哈希函数族, p. 21)

BLAKE2 可通过向 person 参数传入字节串来进行个性化:

>>> from hashlib import blake2b
>>> FILES_HASH_PERSON = b'MyApp Files Hash'
>>> BLOCK_HASH_PERSON = b'MyApp Block Hash'
>>> h = blake2b(digest_size=32, person=FILES_HASH_PERSON)
>>> h.update(b'the same content')
>>> h.hexdigest()
'20d9cd024d4fb086aae819a1432dd2466de12947831b75c5a30cf2676095d3b4'
>>> h = blake2b(digest_size=32, person=BLOCK_HASH_PERSON)
>>> h.update(b'the same content')
>>> h.hexdigest()
'cf68fb5761b9c44e7878bfb2c4c9aea52264a80b75005e65619778de59f383a3'

个性化配合密钥模式也可被用来从单个密钥派生出多个不同密钥。

>>> from hashlib import blake2s
>>> from base64 import b64decode, b64encode
>>> orig_key = b64decode(b'Rm5EPJai72qcK3RGBpW3vPNfZy5OZothY+kHY6h21KM=')
>>> enc_key = blake2s(key=orig_key, person=b'kEncrypt').digest()
>>> mac_key = blake2s(key=orig_key, person=b'kMAC').digest()
>>> print(b64encode(enc_key).decode('utf-8'))
rbPb15S/Z9t+agffno5wuhB77VbRi6F9Iv2qIxU7WHw=
>>> print(b64encode(mac_key).decode('utf-8'))
G9GtHFE1YluXY1zWPlYk1e/nWfu0WSEb0KRcjhDeP/o=
树形模式

以下是对包含两个叶子节点的最小树进行哈希的例子:

  10 /  \00  01

这个例子使用 64 字节内部摘要,返回 32 字节最终摘要:

>>> from hashlib import blake2b
>>>
>>> FANOUT = 2
>>> DEPTH = 2
>>> LEAF_SIZE = 4096
>>> INNER_SIZE = 64
>>>
>>> buf = bytearray(6000)
>>>
>>> # Left leaf
... h00 = blake2b(buf[0:LEAF_SIZE], fanout=FANOUT, depth=DEPTH,
...               leaf_size=LEAF_SIZE, inner_size=INNER_SIZE,
...               node_offset=0, node_depth=0, last_node=False)
>>> # Right leaf
... h01 = blake2b(buf[LEAF_SIZE:], fanout=FANOUT, depth=DEPTH,
...               leaf_size=LEAF_SIZE, inner_size=INNER_SIZE,
...               node_offset=1, node_depth=0, last_node=True)
>>> # Root node
... h10 = blake2b(digest_size=32, fanout=FANOUT, depth=DEPTH,
...               leaf_size=LEAF_SIZE, inner_size=INNER_SIZE,
...               node_offset=0, node_depth=1, last_node=True)
>>> h10.update(h00.digest())
>>> h10.update(h01.digest())
>>> h10.hexdigest()
'3ad2a9b37c6070e374c7a8c508fe20ca86b6ed54e286e93a0318e95e881db5aa'

开发人员

BLAKE2 是由 Jean-Philippe Aumasson, Samuel Neves, Zooko Wilcox-O’HearnChristian Winnerlein 基于 Jean-Philippe Aumasson, Luca Henzen, Willi MeierRaphael C.-W. Phan 所创造的 SHA-3 入围方案 BLAKE 进行设计的。

它使用的核心算法来自由 Daniel J. Bernstein 所设计的 ChaCha 加密。

stdlib 实现是基于 pyblake2 模块的。 它由 Dmitry ChestnykhSamuel Neves 所编写的 C 实现的基础上编写。 此文档拷贝自 pyblake2 并由 Dmitry Chestnykh 撰写。

C 代码由 Christian Heimes 针对 Python 进行了部分的重写。

以下公共领域贡献同时适用于 C 哈希函数实现、扩展代码和本文档:

在法律许可的范围内,作者已将此软件的全部版权以及关联和邻接权利贡献到全球公共领域。 此软件的发布不附带任何担保。

你应该已收到此软件附带的 CC0 公共领域专属证书的副本。 如果没有,请参阅 https://creativecommons.org/publicdomain/zero/1.0/。

根据创意分享公共领域贡献 1.0 通用规范,下列人士为此项目的开发提供了帮助或对公共领域的修改作出了贡献:

  • Alexandr Sokolovskiy

hmac —- 基于密钥的消息验证

源代码: Lib/hmac.py


此模块实现了 HMAC 算法,算法的描述参见 RFC 2104

hmac.new(key, msg=None, digestmod=’’)

返回一个新的 hmac 对象。 key 是一个指定密钥的 bytes 或 bytearray 对象。 如果提供了 msg*,将会调用 update(msg) 方法。 *digestmod 为 HMAC 对象所用的摘要名称、摘要构造器或模块。 它可以是适用于 hashlib.new() 的任何名称。 虽然该参数位置靠后,但它却是必须的。

在 3.4 版更改: 形参 key 可以为 bytes 或 bytearray 对象。 形参 msg 可以为 hashlib 所支持的任意类型。 形参 digestmod 可以为某种哈希算法的名称。

Deprecated since version 3.4, removed in version 3.8: MD5 作为 digestmod 的隐式默认摘要已被弃用。 digestmod 形参现在是必须的。 请将其作为关键字参数传入以避免当你没有初始 msg 时将导致的麻烦。

hmac.digest(key, msg, digest)

基于给定密钥 keydigest 返回 msg 的摘要。 此函数等价于 HMAC(key, msg, digest).digest(),但使用了优化的 C 或内联实现,对放入内存的消息能处理得更快。 形参 key, msgdigest 具有与 new() 中相同的含义。

作为 CPython 的实现细节,优化的 C 实现仅当 digest 为字符串并且是一个 OpenSSL 所支持的摘要算法的名称时才会被使用。

3.7 新版功能.

HMAC 对象具有下列方法:

HMAC.update(msg)

msg 来更新 hmac 对象。 重复调用相当于单次调用并传入所有参数的拼接结果: m.update(a); m.update(b) 等价于 m.update(a + b)

在 3.4 版更改: 形参 msg 可以为 hashlib 所支持的任何类型。

HMAC.digest()

返回当前已传给 update() 方法的字节串数据的摘要。 这个字节串数据的长度将与传给构造器的摘要的长度 digest_size 相同。 它可以包含非 ASCII 的字节,包括 NUL 字节。

警告

在验证例程运行期间将 digest() 的输出与外部提供的摘要进行比较时,建议使用 compare_digest() 函数而不是 == 运算符以减少定时攻击防御力的不足。

HMAC.hexdigest()

类似于 digest() 但摘要会以两倍长度字符串的形式返回,其中仅包含十六进制数码。 这可以被用于在电子邮件或其他非二进制环境中安全地交换数据值。

警告

在验证例程运行期间将 hexdigest() 的输出与外部提供的摘要进行比较时,建议使用 compare_digest() 函数而不是 == 运算符以减少定时攻击防御力的不足。

HMAC.copy()

返回 hmac 对象的副本(“克隆)。 这可被用来高效地计算共享相同初始子串的数据的摘要。

hash 对象具有以下属性:

HMAC.digest_size

以字节表示的结果 HMAC 摘要的大小。

HMAC.block_size

以字节表示的哈希算法的内部块大小。

3.4 新版功能.

HMAC.name

HMAC 的规范名称,总是为小写形式,例如 hmac-md5

3.4 新版功能.

3.9 版后已移除: 未写入文档的属性 HMAC.digest_cons, HMAC.innerHMAC.outer 属于内部实现细节,将在 Python 3.10 中被移除。

这个模块还提供了下列辅助函数:

hmac.compare_digest(a, b)

返回 a == b。 此函数使用一种经专门设计的方式通过避免基于内容的短路行为来防止定时分析,使得它适合处理密码。 ab 必须为相同的类型:或者是 str (仅限 ASCII 字符,如 HMAC.hexdigest() 的返回值),或者是 bytes-like object。

注解

如果 ab 具有不同的长度,或者如果发生了错误,定时攻击在理论上可以获取有关 ab 的类型和长度信息 — 但不能获取它们的值。

3.3 新版功能.

在 3.10 版更改: 此函数在可能的情况下会在内部使用 OpenSSL 的 CRYPTO_memcmp()

secrets —- 生成管理密码的安全随机数

3.6 新版功能.

源代码: Lib/secrets.py


secrets 模块用于生成高度加密的随机数,适于管理密码、账户验证、安全凭据及机密数据。

最好用 secrets 替代 random 模块的默认伪随机数生成器,该生成器适用于建模和模拟,不宜用于安全与加密。

参见

PEP 506

随机数

secrets 模块是操作系统提供的最安全地随机性来源。

class secrets.SystemRandom

用操作系统提供的最高质量源生成随机数的类。

secrets.choice(sequence)

返回从非空序列中随机选取的元素。

secrets.randbelow(n)

返回 [0, n) 范围内的随机整数。

secrets.randbits(k)

返回 k 个随机比特位的整数。

生成 Token

secrets 模块提供了生成安全 Token 的函数,适用于密码重置、密保 URL 等应用场景。

secrets.token_bytes([nbytes=None])

返回含 nbytes 个字节的随机字节字符串。如果未提供 nbytes,或nbytesNone,则使用合理的默认值。

>>> token_bytes(16)  
b'\xebr\x17D*t\xae\xd4\xe3S\xb6\xe2\xebP1\x8b'

secrets.token_hex([nbytes=None])

返回十六进制随机文本字符串。字符串有 nbytes 个随机字节,每个字节转换为两个十六进制数码。未提供 nbytes 或为 None 时,则使用合理的默认值。

>>> token_hex(16)  
'f9bf78b9a18ce6d46a0cd2b0b86df9da'

secrets.token_urlsafe([nbytes=None])

返回安全的 URL 随机文本字符串,包含 nbytes 个随机字节。文本用 Base64 编码,平均来说,每个字节对应 1.3 个结果字符。未提供 nbytes 或为 None 时,则使用合理的默认值。

>>> token_urlsafe(16)  
'Drmhze6EPcv0fN_81Bj-nA'

Token 应当使用多少个字节?

为了在面对 暴力攻击 时保证安全,Token 的随机性必须足够高。随着计算机推衍能力的不断提升,随机性的安全标准也要不断提高。比如 2015 年,32 字节(256 位)的随机性对于 secrets 模块的典型用例就已经足够了。

要自行管理 Token 长度的用户,可以通过为 token_* 函数指定 int 参数显式指定 Token 要使用多大的随机性。该参数以字节数表示随机性大小。

反之,如果未提供参数,或参数为 None,则 token_* 函数将使用合理的默认值。

注解

该默认值随时可能会改变,比如,版本更新的时候。

其他功能

secrets.compare_digest(a, b)

字符串 ab 相等则返回 True,否则返回 False,这种方式可降低 定时攻击 的风险。

应用技巧与最佳实践

本节展示了一些使用 secrets 管理基本安全级别的应用技巧和最佳实践。

生成长度为八个字符的字母数字密码:

import string
import secrets
alphabet = string.ascii_letters + string.digits
password = ''.join(secrets.choice(alphabet) for i in range(8))

注解

应用程序不能 以可恢复的格式存储密码,无论是用纯文本还是加密。 它们应当使用高加密强度的单向(不可恢复)哈希函数来加盐并生成哈希值。

生成长度为十个字符的字母数字密码,包含至少一个小写字母,至少一个大写字母以及至少三个数字:

import string
import secrets
alphabet = string.ascii_letters + string.digits
while True:
    password = ''.join(secrets.choice(alphabet) for i in range(10))
    if (any(c.islower() for c in password)
            and any(c.isupper() for c in password)
            and sum(c.isdigit() for c in password) >= 3):
        break

生成 XKCD 风格的密码串

import secrets
# On standard Linux systems, use a convenient dictionary file.
# Other platforms may need to provide their own word-list.
with open('/usr/share/dict/words') as f:
    words = [word.strip() for word in f]
    password = ' '.join(secrets.choice(words) for i in range(4))

生成临时密保 URL,包含密码恢复应用的安全 Token:

import secrets
url = 'https://mydomain.com/reset=' + secrets.token_urlsafe()

通用操作系统服务

  • os —- 多种操作系统接口
    • 文件名,命令行参数,以及环境变量。
    • Python UTF-8 模式
    • 进程参数
    • 创建文件对象
    • 文件描述符操作
      • 查询终端的尺寸
      • 文件描述符的继承
    • 文件和目录
      • Linux 扩展属性
    • 进程管理
    • 调度器接口
    • 其他系统信息
    • 随机数
  • io —- 处理流的核心工具
    • 概述
      • 文本 I/O
      • 二进制 I/O
      • 原始 I/O
    • 文本编码格式
      • 选择性的 EncodingWarning
    • 高阶模块接口
    • 类的层次结构
      • I/O 基类
      • 原始文件 I/O
      • 缓冲流
      • 文本 I/O
    • 性能
      • 二进制 I/O
      • 文本 I/O
      • 多线程
      • 可重入性
  • time —- 时间的访问和转换
    • 函数
    • Clock ID 常量
    • 时区常量
  • argparse —- 命令行选项、参数和子命令解析器
    • 示例
      • 创建一个解析器
      • 添加参数
      • 解析参数
    • ArgumentParser 对象
      • prog
      • usage
      • description
      • epilog
      • parents
      • formatter_class
      • prefix_chars
      • fromfile_prefix_chars
      • argument_default
      • allow_abbrev
      • conflict_handler
      • add_help
      • exit_on_error
    • add_argument() 方法
      • name or flags
      • action
      • nargs
      • const
      • 默认值
      • type — 类型
      • choices
      • required
      • help
      • metavar
      • dest
      • Action 类
    • parse_args() 方法
      • 选项值语法
      • 无效的参数
      • 包含 - 的参数
      • 参数缩写(前缀匹配)
      • sys.argv 以外
      • 命名空间对象
    • 其它实用工具
      • 子命令
      • FileType 对象
      • 参数组
      • 互斥
      • 解析器默认值
      • 打印帮助
      • 部分解析
      • 自定义文件解析
      • 退出方法
      • 混合解析
    • 升级 optparse 代码
  • getopt —- C 风格的命令行选项解析器
  • logging —- Python 的日志记录工具
    • 记录器对象
    • 日志级别
    • 处理器对象
    • 格式器对象
    • 过滤器对象
    • LogRecord 属性
    • LogRecord 属性
    • LoggerAdapter 对象
    • 线程安全
    • 模块级函数
    • 模块级属性
    • 与警告模块集成
  • logging.config —- 日志记录配置
    • 配置函数
    • 配置字典架构
      • 字典架构细节
      • 增量配置
      • 对象连接
      • 用户定义对象
      • 访问外部对象
      • 访问内部对象
      • 导入解析与定制导入器
    • 配置文件格式
  • logging.handlers —- 日志处理程序
    • StreamHandler
    • FileHandler
    • NullHandler
    • WatchedFileHandler
    • BaseRotatingHandler
    • RotatingFileHandler
    • TimedRotatingFileHandler
    • SocketHandler
    • DatagramHandler
    • SysLogHandler
    • NTEventLogHandler
    • SMTPHandler
    • MemoryHandler
    • HTTPHandler
    • QueueHandler
    • QueueListener
  • getpass —- 便携式密码输入工具
  • curses —- 终端字符单元显示的处理
    • 函数
    • Window 对象
    • 常量
  • curses.textpad —- 用于 curses 程序的文本输入控件
    • 文本框对象
  • curses.ascii —- 用于 ASCII 字符的工具
  • curses.panel —- curses 的面板栈扩展
    • 函数
    • Panel 对象
  • platform —- 获取底层平台的标识数据
    • 跨平台
    • Java平台
    • Windows平台
    • macOS Platform
    • Unix 平台
    • Linux 平台
  • errno —- 标准 errno 系统符号
  • ctypes —- Python 的外部函数库
    • ctypes 教程
      • 载入动态连接库
      • 操作导入的动态链接库中的函数
      • 调用函数
      • 基础数据类型
      • 调用函数,继续
      • 使用自定义的数据类型调用函数
      • 指定必选参数的类型(函数原型)
      • 返回类型
      • 传递指针(或以引用方式传递形参)
      • 结构体和联合
      • 结构体/联合字段对齐及字节顺序
      • 结构体和联合中的位域
      • 数组
      • 指针
      • 类型转换
      • 不完整类型
      • 回调函数
      • 访问 dll 的导出变量
      • 意外
      • 变长数据类型
    • ctypes 参考手册
      • 寻找动态链接库
      • 加载动态链接库
      • 外部函数
      • 函数原型
      • 工具函数
      • 数据类型
      • 基础数据类型
      • 结构化数据类型
      • 数组与指针

os —- 多种操作系统接口

源代码: Lib/os.py


本模块提供了一种使用与操作系统相关的功能的便捷式途径。

关于这些函数的适用性的说明:

  • Python中所有依赖于操作系统的内置模块的设计都是这样,只要不同的操作系统某一相同的功能可用,它就使用相同的接口。例如,函数 os.stat(path) 以相同的格式返回关于 path 的状态信息(该格式源于 POSIX 接口)。
  • 特定于某一操作系统的扩展通过操作 os 模块也是可用的,但是使用它们当然是对可移植性的一种威胁。
  • 所有接受路径或文件名的函数都同时支持字节串和字符串对象,并在返回路径或文件名时使用相应类型的对象作为结果。
  • 在 VxWorks 系统上,os.popen, os.fork, os.execv 和 os.spawnp 都未支持。

注解

如果使用无效或无法访问的文件名与路径,或者其他类型正确但操作系统不接受的参数,此模块的所有函数都抛出 OSError (或者它的子类)。

exception os.error

内建的 OSError 异常的一个别名。

os.name

导入的依赖特定操作系统的模块的名称。以下名称目前已注册: 'posix', 'nt', 'java'.

文件名,命令行参数,以及环境变量。

在 Python 中,使用字符串类型表示文件名、命令行参数和环境变量。 在某些系统上,在将这些字符串传递给操作系统之前,必须将这些字符串解码为字节。 Python 使用 filesystem encoding and error handler 来执行此转换。

filesystem encoding and error handler 是在 Python 启动时通过 PyConfig_Read() 函数来配置的。

在 3.1 版更改: 在某些系统上,使用文件系统编码进行转换可能会失败。 在这种情况下,Python 会使用 代理转义编码错误处理器,这意味着在解码时,不可解码的字节被 Unicode 字符 U+DCxx 替换,并且这些字节在编码时再次转换为原始字节。

文件系统编码器 必须保证能成功解码所有 128 以内的字节。如果不能保证,API 函数可能触发 UnicodeError

Python UTF-8 模式

3.7 新版功能: 有关更多详细信息,请参阅 PEP 540

Python UTF-8 模式会忽略 locale encoding 并强制使用 UTF-8 编码。

  • 用 UTF-8 作为 文件系统编码。
  • sys.getfilesystemencoding() 返回 'UTF-8'
  • locale.getpreferredencoding() 返回 'UTF-8' (do_setlocale 参数不起作用)。
  • sys.stdin, sys.stdoutsys.stderr 都将 UTF-8 用作它们的文本编码,并且为 sys.stdinsys.stdout 启用 surrogateescape 错误处理句柄 (sys.stderr 会继续使用 backslashreplace 如同在默认的局部感知模式下一样)
  • 在 Unix 中,os.device_encoding() 返回 'UTF-8'。而不是设备的编码。

请注意 UTF-8 模式下的标准流设置可以被 PYTHONIOENCODING 所覆盖(在默认的区域感知模式下也同样如此)。

作为低层级 API 发生改变的结果,其他高层级 API 也会表现出不同的默认行为:

  • 命令行参数,环境变量和文件名会使用 UTF-8 编码来解码为文本。
  • os.fsdecode()os.fsencode() 会使用 UTF-8 编码。
  • open(), io.open()codecs.open() 默认会使用 UTF-8 编码。 但是,它们默认仍将使用严格错误处理句柄,因此试图在文本模式下打开二进制文件将可能引发异常,而不是生成无意义的数据。

如果在 Python 启动时 LC_CTYPE 区域设为 CPOSIX ,则启用 Python UTF-8 模式 (参见 PyConfig_Read() 函数)。

它可以通过命令行选项 -X utf8 和环境变量 PYTHONUTF8,来启用或禁用。

如果没有设置 PYTHONUTF8 环境变量,那么解释器默认使用当前的地区设置,除非 当前地区识别为基于 ASCII 的传统地区(如 PYTHONCOERCECLOCALE 所述),并且 locale coercion 被禁用或失败。在这种传统地区,除非显式指明不要如此,解释器将默认启用 UTF-8 模式。

Python UTF-8 模式只能在 Python 启动时启用。其值可以从 sys.flags.utf8_mode 读取。

进程参数

这些函数和数据项提供了操作当前进程和用户的信息。

os.ctermid()

返回与进程控制终端对应的文件名。

可用性: Unix。

os.environ

一个表示字符串环境的 mapping 对象。 例如,environ['HOME'] 是你的主目录(在某些平台上)的路径名,相当于 C 中的 getenv("HOME")

这个映射是在第一次导入 os 模块时捕获的,通常作为 Python 启动时处理 site.py 的一部分。除了通过直接修改 os.environ 之外,在此之后对环境所做的更改不会反映在 os.environ 中。

该映射除了可以用于查询环境外,还能用于修改环境。当该映射被修改时,将自动调用 putenv()

在Unix系统上,键和值会使用 sys.getfilesystemencoding()'surrogateescape' 的错误处理。如果你想使用其他的编码,使用 environb

注解

直接调用 putenv() 并不会影响 os.environ,所以推荐直接修改 os.environ

注解

在某些平台上,包括 FreeBSD 和 macOS,设置 environ 可能导致内存泄漏。 请参阅 putenv() 的系统文档。

可以删除映射中的元素来删除对应的环境变量。当从 os.environ 删除元素时,以及调用 pop()clear() 之一时,将自动调用 unsetenv()

在 3.9 版更改: 已更新并支持了 PEP 584 的合并 (|) 和更新 (|=) 运算符。

os.environb

字节版本的 environ: 一个以字节串表示环境的 mapping 对象。 environenvironb 是同步的(修改 environb 会更新 environ,反之亦然)。

只有在 supports_bytes_environTrue 的时候 environb 才是可用的。

3.2 新版功能.

在 3.9 版更改: 已更新并支持了 PEP 584 的合并 (|) 和更新 (|=) 运算符。

os.chdir(path)

os.fchdir(fd)

os.getcwd()

os.fsencode(filename)

将 类似路径形式的 filename 编码为 filesystem encoding and error handler;原样返回 bytes

fsdecode() 是此函数的逆向函数。

3.2 新版功能.

在 3.6 版更改: 增加对实现了 os.PathLike 接口的对象的支持。

os.fsdecode(filename)

根据 filesystem encoding and error handler 来解码 类似路径形式的 filename;原样返回 str

fsencode() 是此函数的逆向函数。

3.2 新版功能.

在 3.6 版更改: 增加对实现了 os.PathLike 接口的对象的支持。

os.fspath(path)

返回路径的文件系统表示。

如果传入的是 strbytes 类型的字符串,将原样返回。否则 __fspath__() 将被调用,如果得到的是一个 strbytes 类型的对象,那就返回这个值。其他所有情况则会抛出 TypeError 异常。

3.6 新版功能.

class os.PathLike

某些对象用于表示文件系统中的路径(如 pathlib.PurePath 对象),本类是这些对象的 抽象基类。

3.6 新版功能.

  • abstractmethod __fspath__()

    返回当前对象的文件系统表示。

    这个方法只应该返回一个 str 字符串或 bytes 字节串,请优先选择 str 字符串。

os.getenv(key, default=None)

如果存在,返回环境变量 key 的值,否则返回 default*。 *keydefault 和返回值均为 str 字符串类型。

在Unix系统上,键和值会使用 sys.getfilesystemencoding()‘surrogateescape’ 错误处理进行解码。如果你想使用其他的编码,使用 os.getenvb()

可用性: 大部分的Unix系统,Windows。

os.getenvb(key, default=None)

如果存在环境变量 key 那么返回其值,否则返回 default*。 *keydefault 和返回值均为bytes字节串类型。

getenvb() 仅在 supports_bytes_environTrue 时可用。

可用性: 大部分的Unix系统。

3.2 新版功能.

os.get_exec_path(env=None)

返回将用于搜索可执行文件的目录列表,与在外壳程序中启动一个进程时相似。指定的 env 应为用于搜索 PATH 的环境变量字典。默认情况下,当 envNone 时,将会使用 environ

3.2 新版功能.

os.getegid()

返回当前进程的有效组ID。对应当前进程执行文件的 “set id” 位。

可用性: Unix。

os.geteuid()

返回当前进程的有效用户ID。

可用性: Unix。

os.getgid()

返回当前进程的实际组ID。

可用性: Unix。

os.getgrouplist(user, group)

返回该用户所在的组 ID 列表。可能 group 参数没有在返回的列表中,实际上用户应该也是属于该 groupgroup 参数一般可以从储存账户信息的密码记录文件中找到。

可用性: Unix。

3.3 新版功能.

os.getgroups()

返回当前进程关联的附加组ID列表

可用性: Unix。

注解

在 macOS 中,getgroups() 会和其他 Unix 平台有所不同。 如果 Python 解释器是在 10.5 或更早版本中部署的,则 getgroups() 会返回与当前用户进程相关联的有效组 ID 列表;该列表受限于系统预定义的条目数量,通常为 16,并且在适当的权限下还可通过调用 setgroups() 来修改。 如果是在高于 10.5 的版本中部署的,则 getgroups() 会返回与进程的有效用户 ID 相关联的当前组访问列表;组访问列表可能会在进程的生命周期之内发生改变,它不会受对 setgroups() 的调用影响,且其长度也不被限制为 16。 部署目标值 MACOSX_DEPLOYMENT_TARGET 可以通过 sysconfig.get_config_var() 来获取。

os.getlogin()

返回通过控制终端进程进行登录的用户名。在多数情况下,使用 getpass.getuser() 会更有效,因为后者会通过检查环境变量 LOGNAMEUSERNAME 来查找用户,再由 pwd.getpwuid(os.getuid())[0] 来获取当前用户 ID 的登录名。

可用性: Unix, Windows。

os.getpgid(pid)

根据进程id pid 返回进程的组 ID 列表。如果 pid 为 0,则返回当前进程的进程组 ID 列表

可用性: Unix。

os.getpgrp()

返回当时进程组的ID

可用性: Unix。

os.getpid()

返回当前进程ID

os.getppid()

返回父进程ID。当父进程已经结束,在Unix中返回的ID是初始进程(1)中的一个,在Windows中仍然是同一个进程ID,该进程ID有可能已经被进行进程所占用。

可用性: Unix, Windows。

在 3.2 版更改: 添加WIndows的支持。

os.getpriority(which, who)

获取程序调度优先级。which 参数值可以是 PRIO_PROCESSPRIO_PGRP,或 PRIO_USER 中的一个,who 是相对于 which (PRIO_PROCESS 的进程标识符,PRIO_PGRP 的进程组标识符和 PRIO_USER 的用户ID)。当 who 为 0 时(分别)表示调用的进程,调用进程的进程组或调用进程所属的真实用户 ID。

可用性: Unix。

3.3 新版功能.

os.PRIO_PROCESS
os.PRIO_PGRP
os.PRIO_USER

函数 getpriority()setpriority() 的参数。

可用性: Unix。

3.3 新版功能.

os.getresuid()

返回一个由 (ruid, euid, suid) 所组成的元组,分别表示当前进程的真实用户ID,有效用户ID和暂存用户ID。

可用性: Unix。

3.2 新版功能.

os.getresgid()

返回一个由 (rgid, egid, sgid) 所组成的元组,分别表示当前进程的真实组ID,有效组ID和暂存组ID。

可用性: Unix。

3.2 新版功能.

os.getuid()

返回当前进程的真实用户ID。

可用性: Unix。

os.initgroups(username, gid)

调用系统 initgroups(),使用指定用户所在的所有值来初始化组访问列表,包括指定的组ID。

可用性: Unix。

3.2 新版功能.

os.putenv(key, value)

将名为 key 的环境变量值设置为 value。该变量名修改会影响由 os.system()popen()fork()execv() 发起的子进程。

os.environ 中的参数赋值会自动转换为对 putenv() 的调用。不过 putenv() 的调用不会更新 os.environ,因此最好使用 os.environ 对变量赋值。

注解

在某些平台上,包括 FreeBSD 和 macOS,设置 environ 可能导致内存泄漏。 请参阅 putenv() 的系统文档。

引发一个 审计事件 os.putenv,附带参数 key, value

在 3.9 版更改: 该函数现在总是可用。

os.setegid(egid)

设置当前进程的有效组ID。

可用性: Unix。

os.seteuid(euid)

设置当前进程的有效用户ID。

可用性: Unix。

os.setgid(gid)

设置当前进程的组ID。

可用性: Unix。

os.setgroups(groups)

group 参数值设置为与当进程相关联的附加组ID列表。group 参数必须为一个序列,每个元素应为每个组的数字ID。该操作通常只适用于超级用户。

可用性: Unix。

注解

在 macOS 中,groups 的长度不能超过系统定义的最大有效组 ID 数量,通常为 16。 对于未返回与调用 setgroups() 产生的相同组列表的情况。

os.setpgrp()

根据已实现的版本(如果有)来调用系统 setpgrp()setpgrp(0, 0) 。相关说明,请参考 Unix 手册。

可用性: Unix。

os.setpgid(pid, pgrp)

使用系统调用 setpgid(),将 pid 对应进程的组ID设置为 pgrp。相关说明,请参考 Unix 手册。

可用性: Unix。

os.setpriority(which, who, priority)

设置程序调度优先级。 which 的值为 PRIO_PROCESS, PRIO_PGRPPRIO_USER 之一,而 who 会相对于 which (PRIO_PROCESS 的进程标识符, PRIO_PGRP 的进程组标识符和 PRIO_USER 的用户 ID) 被解析。 who 值为零 (分别) 表示调用进程,调用进程的进程组或调用进程的真实用户 ID。 priority 是范围在 -20 至 19 的值。 默认优先级为 0;较小的优先级数值会更优先被调度。

可用性: Unix。

3.3 新版功能.

os.setregid(rgid, egid)

设置当前进程的真实和有效组ID。

可用性: Unix。

os.setresgid(rgid, egid, sgid)

设置当前进程的真实,有效和暂存组ID。

可用性: Unix。

3.2 新版功能.

os.setresuid(ruid, euid, suid)

设置当前进程的真实,有效和暂存用户ID。

可用性: Unix。

3.2 新版功能.

os.setreuid(ruid, euid)

设置当前进程的真实和有效用户ID。

可用性: Unix。

os.getsid(pid)

调用系统调用 getsid()。相关说明,请参考 Unix 手册。

可用性: Unix。

os.setsid()

使用系统调用 getsid()。相关说明,请参考 Unix 手册。

可用性: Unix。

os.setuid(uid)

设置当前进程的用户ID。

可用性: Unix。

os.strerror(code)

根据 code 中的错误码返回错误消息。 在某些平台上当给出未知错误码时 strerror() 将返回 NULL 并会引发 ValueError

os.supports_bytes_environ

如果操作系统上原生环境类型是字节型则为 True (例如在 Windows 上为 False)。

3.2 新版功能.

os.umask(mask)

设定当前数值掩码并返回之前的掩码。

os.uname()

返回当前操作系统的识别信息。返回值是一个有5个属性的对象:

  • sysname - 操作系统名
  • nodename - 机器在网络上的名称(需要先设定)
  • release - 操作系统发行信息
  • version - 操作系统版本信息
  • machine - 硬件标识符

为了向后兼容,该对象也是可迭代的,像是一个按照 sysnamenodenamereleaseversion,和 machine 顺序组成的元组。

有些系统会将 nodename 截短为 8 个字符或截短至前缀部分;获取主机名的一个更好方式是 socket.gethostname() 或甚至可以用 socket.gethostbyaddr(socket.gethostname())

可用性: 较新的 Unix 版本。

在 3.3 版更改: 返回结果的类型由元组变成一个类似元组的对象,同时具有命名的属性。

os.unsetenv(key)

取消设置(删除)名为 key 的环境变量。变量名的改变会影响由 os.system()popen()fork()execv() 触发的子进程。

删除在 os.environ 中的变量会自动转换为对 unsetenv() 的调用。但是 unsetenv() 不能更新 os.environ,因此最好直接删除 os.environ 中的变量。

引发一个 审计事件 os.unsetenv,附带参数 key

在 3.9 版更改: 该函数现在总是可用,并且在 Windows 上也可用。

创建文件对象

这些函数创建新的 file objects 。

os.fdopen(fd, \args, *kwargs)

返回打开文件描述符 fd 对应文件的对象。类似内建 open() 函数,二者接受同样的参数。不同之处在于 fdopen() 第一个参数应该为整数。

文件描述符操作

这些函数对文件描述符所引用的 I/O 流进行操作。

文件描述符是一些小的整数,对应于当前进程所打开的文件。例如,标准输入的文件描述符通常是0,标准输出是1,标准错误是2。之后被进程打开的文件的文件描述符会被依次指定为3,4,5等。“文件描述符”这个词有点误导性,在 Unix 平台中套接字和管道也被文件描述符所引用。

当需要时,可以用 fileno() 可以获得 file object 所对应的文件描述符。需要注意的是,直接使用文件描述符会绕过文件对象的方法,会忽略如数据内部缓冲等情况。

os.close(fd)

关闭文件描述符 fd

注解

该功能适用于低级 I/O 操作,必须用于 os.open()pipe() 返回的文件描述符。若要关闭由内建函数 open()popen()fdopen() 返回的 “文件对象”,则应使用其相应的 close() 方法。

os.closerange(fd_low, fd_high)

关闭从 fd_low (包括)到 fd_high (排除)间的文件描述符,并忽略错误。类似(但快于):

for fd in range(fd_low, fd_high):
    try:
        os.close(fd)
    except OSError:
        pass

os.copy_file_range(src, dst, count, offset_src=None, offset_dst=None)

从文件描述符 src 复制 count 字节,从偏移量 offset_src 开始读取,到文件描述符 dst*,从偏移量 *offset_dst 开始写入。如果 offset_src 为 None,则 src 将从当前位置开始读取;offset_dst 同理。srcdst 指向的文件必须处于相同的文件系统,否则将会抛出一个 errno 被设为 errno.EXDEVOSError

此复制的完成没有额外的从内核到用户空间再回到内核的数据转移花费。另外,一些文件系统可能实现额外的优化。完成复制就如同打开两个二进制文件一样。

返回值是复制的字节的数目。这可能低于需求的数目。

Availability: Linux kernel >= 4.5 或 glibc >= 2.27。

3.8 新版功能.

os.device_encoding(fd)

如果连接到终端,则返回一个与 fd 关联的设备描述字符,否则返回 None

在 Unix 上,如果启用了 Python UTF-8 模式,则返回 'UTF-8' 而不是设备的编码格式。

在 3.10 版更改: 在 Unix 上,该函数现在实现了 Python UTF-8 模式。

os.dup(fd)

返回一个文件描述符 fd 的副本。该文件描述符的副本是 不可继承的。

在 Windows 中,当复制一个标准流(0: stdin, 1: stdout, 2: stderr)时,新的文件描述符是 可继承的。

在 3.4 版更改: 新的文件描述符现在是不可继承的。

os.dup2(fd, fd2, inheritable=True)

把文件描述符 fd 复制为 fd2,必要时先关闭后者。返回 *fd2。新的文件描述符默认是 可继承的,除非在 *inheritableFalse 时,是不可继承的。

在 3.4 版更改: 添加可选参数 inheritable

在 3.7 版更改: 成功时返回 fd2,以过去的版本中,总是返回 None

os.fchmod(fd, mode)

fd 指定文件的权限状态修改为 mode*。可以参考 chmod() 中列出 *mode 的可用值。从Python 3.3开始,这相当于 os.chmod(fd, mode)

引发一个 审计事件 os.chmod,附带参数 pathmodedir_fd

可用性: Unix。

os.fchown(fd, uid, gid)

分别将 fd 指定文件的所有者和组 ID 修改为 uidgid 的值。若不想变更其中的某个 ID,可将相应值设为 -1。从 Python 3.3 开始,这相当于 os.chown(fd, uid, gid)

引发一个 审计事件 os.chown,附带参数 pathuidgiddir_fd

可用性: Unix。

os.fdatasync(fd)

强制将文件描述符 fd 指定文件写入磁盘。不强制更新元数据。

可用性: Unix。

注解

该功能在 MacOS 中不可用。

os.fpathconf(fd, name)

返回与打开的文件有关的系统配置信息。name 指定要查找的配置名称,它可以是字符串,是一个系统已定义的名称,这些名称定义在不同标准(POSIX.1,Unix 95,Unix 98 等)中。一些平台还定义了额外的其他名称。当前操作系统已定义的名称在 pathconf_names 字典中给出。对于未包含在该映射中的配置名称,也可以传递一个整数作为 name

如果 name 是一个字符串且不是已定义的名称,将抛出 ValueError 异常。如果当前系统不支持 name 指定的配置名称,即使该名称存在于 pathconf_names,也会抛出 OSError 异常,错误码为 errno.EINVAL

从 Python 3.3 起,此功能等价于 os.pathconf(fd, name)

可用性: Unix。

os.fstat(fd)

获取文件描述符 fd 的状态. 返回一个 stat_result 对象。

从 Python 3.3 起,此功能等价于 os.stat(fd)

参见

stat() 函数。

os.fstatvfs(fd)

返回文件系统的信息,该文件系统是文件描述符 fd 指向的文件所在的文件系统,与 statvfs() 一样。从 Python 3.3 开始,它等效于 os.statvfs(fd)

可用性: Unix。

os.fsync(fd)

强制将文件描述符 fd 指向的文件写入磁盘。在 Unix,这将调用原生 fsync() 函数;在 Windows,则是 MS _commit() 函数。

如果要写入的是缓冲区内的 Python 文件对象 f,请先执行 f.flush(),然后执行 os.fsync(f.fileno()),以确保与 f 关联的所有内部缓冲区都写入磁盘。

可用性: Unix, Windows。

os.ftruncate(fd, length)

截断文件描述符 fd 指向的文件,以使其最大为 length 字节。从 Python 3.3 开始,它等效于 os.truncate(fd, length)

引发一个 审计事件 os.truncate,附带参数 fd, length

可用性: Unix, Windows。

在 3.5 版更改: 添加了 Windows 支持

os.get_blocking(fd)

获取文件描述符的阻塞模式:如果设置了 O_NONBLOCK 标志位,返回 False,如果该标志位被清除,返回 True

参见 set_blocking()socket.socket.setblocking()

可用性: Unix。

3.5 新版功能.

os.isatty(fd)

如果文件描述符 fd 打开且已连接至 tty 设备(或类 tty 设备),返回 True,否则返回 False

os.lockf(fd, cmd, len)

在打开的文件描述符上,使用、测试或删除 POSIX 锁。fd 是一个打开的文件描述符。cmd 指定要进行的操作,它们是 F_LOCKF_TLOCKF_ULOCKF_TEST 中的一个。len 指定哪部分文件需要锁定。

引发一个 审计事件 os.lockf,附带参数 fdcmdlen

可用性: Unix。

3.3 新版功能.

os.F_LOCK
os.F_TLOCK
os.F_ULOCK
os.F_TEST

标志位,用于指定 lockf() 进行哪一种操作。

可用性: Unix。

3.3 新版功能.

os.lseek(fd, pos, how)

将文件描述符 fd 的当前位置设置为 pos*,位置的计算方式 *how 如下:设置为 SEEK_SET0 表示从文件开头计算,设置为 SEEK_CUR1 表示从文件当前位置计算,设置为 SEEK_END2 表示文件末尾计算。返回新指针位置,这个位置是从文件开头计算的,单位是字节。

os.SEEK_SET
os.SEEK_CUR
os.SEEK_END

lseek() 函数的参数,它们的值分别为 0、1 和 2。

3.3 新版功能: 某些操作系统可能支持其他值,例如 os.SEEK_HOLEos.SEEK_DATA

os.open(path, flags, mode=511, **, dir_fd=None*)

打开文件 path*,根据 *flags 设置各种标志位,并根据 mode 设置其权限状态。当计算 mode 时,会首先根据当前 umask 值将部分权限去除。本方法返回新文件的描述符。新的文件描述符是 不可继承 的。

有关 flag 和 mode 取值的说明,请参见 C 运行时文档。标志位常量(如 O_RDONLYO_WRONLY)在 os 模块中定义。特别地,在 Windows 上需要添加 O_BINARY 才能以二进制模式打开文件。

本函数带有 dir_fd 参数,支持 基于目录描述符的相对路径。

open 附带参数 pathmodeflags 会引发 审计事件。

在 3.4 版更改: 新的文件描述符现在是不可继承的。

注解

本函数适用于底层的 I/O。常规用途请使用内置函数 open(),该函数的 read()write() 方法(及其他方法)会返回 文件对象。要将文件描述符包装在文件对象中,请使用 fdopen()

3.3 新版功能: dir_fd 参数。

在 3.5 版更改: 如果系统调用被中断,但信号处理程序没有触发异常,此函数现在会重试系统调用,而不是触发 InterruptedError 异常 (原因详见 PEP 475)。

在 3.6 版更改: 接受一个 path-like object。

以下常量是 open() 函数 flags 参数的选项。可以用按位或运算符 | 将它们组合使用。部分常量并非在所有平台上都可用。有关其可用性和用法的说明或 MSDN (Windows 上)。

os.O_RDONLY
os.O_WRONLY
os.O_RDWR
os.O_APPEND
os.O_CREAT
os.O_EXCL
os.O_TRUNC

上述常量在 Unix 和 Windows 上均可用。

os.O_DSYNC
os.O_RSYNC
os.O_SYNC
os.O_NDELAY
os.O_NONBLOCK
os.O_NOCTTY
os.O_CLOEXEC

这个常数仅在 Unix 系统中可用。

在 3.3 版更改: 增加 O_CLOEXEC 常量。

os.O_BINARY
os.O_NOINHERIT
os.O_SHORT_LIVED
os.O_TEMPORARY
os.O_RANDOM
os.O_SEQUENTIAL
os.O_TEXT

这个常数仅在 Windows 系统中可用。

os.O_EVTONLY
os.O_FSYNC
os.O_SYMLINK
os.O_NOFOLLOW_ANY

以上常量仅适用于 macOS。

在 3.10 版更改: 加入 O_EVTONLYO_FSYNCO_SYMLINKO_NOFOLLOW_ANY 常量。

os.O_ASYNC
os.O_DIRECT
os.O_DIRECTORY
os.O_NOFOLLOW
os.O_NOATIME
os.O_PATH
os.O_TMPFILE
os.O_SHLOCK
os.O_EXLOCK

上述常量是扩展常量,如果 C 库未定义它们,则不存在。

在 3.4 版更改: 在支持的系统上增加 O_PATH。增加 O_TMPFILE,仅在 Linux Kernel 3.11 或更高版本可用。

os.openpty()

打开一对新的伪终端,返回一对文件描述符 (主,从),分别为 pty 和 tty。新的文件描述符是 不可继承 的。对于(稍微)轻量一些的方法,请使用 pty 模块。

可用性: 某些 Unix。

在 3.4 版更改: 新的文件描述符不再可继承。

os.pipe()

创建一个管道,返回一对分别用于读取和写入的文件描述符 (r, w)。新的文件描述符是 不可继承 的。

可用性: Unix, Windows。

在 3.4 版更改: 新的文件描述符不再可继承。

os.pipe2(flags)

创建带有 flags 标志位的管道。可通过对以下一个或多个值进行“或”运算来构造这些 flagsO_NONBLOCKO_CLOEXEC。返回一对分别用于读取和写入的文件描述符 (r, w)

可用性: 某些 Unix。

3.3 新版功能.

os.posix_fallocate(fd, offset, len)

确保为 fd 指向的文件分配了足够的磁盘空间,该空间从偏移量 offset 开始,到 len 字节为止。

可用性: Unix。

3.3 新版功能.

os.posix_fadvise(fd, offset, len, advice)

声明即将以特定模式访问数据,使内核可以提前进行优化。数据范围是从 fd 所指向文件的 offset 开始,持续 len 个字节。advice 的取值是如下之一:POSIX_FADV_NORMAL, POSIX_FADV_SEQUENTIAL, POSIX_FADV_RANDOM, POSIX_FADV_NOREUSE, POSIX_FADV_WILLNEEDPOSIX_FADV_DONTNEED

可用性: Unix。

3.3 新版功能.

os.POSIX_FADV_NORMAL
os.POSIX_FADV_SEQUENTIAL
os.POSIX_FADV_RANDOM
os.POSIX_FADV_NOREUSE
os.POSIX_FADV_WILLNEED
os.POSIX_FADV_DONTNEED

用于 posix_fadvise()advice 参数的标志位,指定可能使用的访问模式。

可用性: Unix。

3.3 新版功能.

os.pread(fd, n, offset)

从文件描述符 fd 所指向文件的偏移位置 offset 开始,读取至多 n 个字节,而保持文件偏移量不变。

返回所读取字节的字节串 (bytestring)。如果到达了 fd 指向的文件末尾,则返回空字节对象。

可用性: Unix。

3.3 新版功能.

os.preadv(fd, buffers, offset, flags=0)

从文件描述符 fd 所指向文件的偏移位置 offset 开始,将数据读取至可变 字节类对象 缓冲区 buffers 中,保持文件偏移量不变。将数据依次存放到每个缓冲区中,填满一个后继续存放到序列中的下一个缓冲区,来保存其余数据。

flags 参数可以由零个或多个标志位进行按位或运算来得到:

  • RWF_HIPRI
  • RWF_NOWAIT

返回实际读取的字节总数,该总数可以小于所有对象的总容量。

操作系统可能对允许使用的缓冲区数量有限制(使用 sysconf() 获取 'SC_IOV_MAX' 值)。

本方法结合了 os.readv()os.pread() 的功能。

可用性:Linux 2.6.30 或更高版本,FreeBSD 6.0 或更高版本,OpenBSD 2.7 或更高版本,AIX 7.1 或更高版本。使用标志位需要 Linux 4.6 或更高版本。

3.7 新版功能.

os.RWF_NOWAIT

不要等待无法立即获得的数据。如果指定了此标志,那么当需要从后备存储器中读取数据,或等待文件锁时,系统调用将立即返回。

如果成功读取数据,则返回读取的字节数。如果未读取到数据,则返回 -1,并将错误码 errno 置为 errno.EAGAIN

可用性:Linux 4.14 或更高版本。

3.7 新版功能.

os.RWF_HIPRI

高优先级读/写。允许基于块的文件系统对设备进行轮询,这样可以降低延迟,但可能会占用更多资源。

目前在 Linux 上,此功能仅在使用 O_DIRECT 标志打开的文件描述符上可用。

可用性:Linux 4.6 或更高版本。

3.7 新版功能.

os.pwrite(fd, str, offset)

str 中的字节串 (bytestring) 写入文件描述符 fd 的偏移位置 offset 处,保持文件偏移量不变。

返回实际写入的字节数。

可用性: Unix。

3.3 新版功能.

os.pwritev(fd, buffers, offset, flags=0)

将缓冲区 buffers 的内容写入文件描述符 fd 的偏移位置 offset 处,保持文件偏移量不变。缓冲区 buffers 必须是由 字节类对象 组成的序列。缓冲区以数组顺序处理。先写入第一个缓冲区的全部内容,再写入第二个缓冲区,照此继续。

flags 参数可以由零个或多个标志位进行按位或运算来得到:

  • RWF_DSYNC
  • RWF_SYNC
  • RWF_APPEND

返回实际写入的字节总数。

操作系统可能对允许使用的缓冲区数量有限制(使用 sysconf() 获取 'SC_IOV_MAX' 值)。

本方法结合了 os.writev()os.pwrite() 的功能。

可用性:Linux 2.6.30 或更高版本,FreeBSD 6.0 或更高版本,OpenBSD 2.7 或更高版本,AIX 7.1 或更高版本。使用标志位需要 Linux 4.7 或更高版本。

3.7 新版功能.

os.RWF_DSYNC

提供预写功能,等效于带 O_DSYNC 标志的 os.open() 。本标志只作用于通过系统调用写入的数据。

可用性:Linux 4.7 或更高版本。

3.7 新版功能.

os.RWF_SYNC

提供预写功能,等效于带 O_SYNC 标志的 os.open() 。本标志只作用于通过系统调用写入的数据。

可用性:Linux 4.7 或更高版本。

3.7 新版功能.

os.RWF_APPEND

提供预写功能,等效于带 O_APPEND 标志的 os.open() 。本标志只对 os.pwritev() 有意义,只作用于通过系统调用写入的数据。参数 offset 对写入操作无效;数据总是会添加到文件的末尾。但如果 offset 参数为 -1,则会刷新当前文件的 offset

可用性:Linux 4.16 以上版本。

3.10 新版功能.

os.read(fd, n)

从文件描述符 fd 中读取至多 n 个字节。

返回所读取字节的字节串 (bytestring)。如果到达了 fd 指向的文件末尾,则返回空字节对象。

注解

该功能适用于低级 I/O 操作,必须用于 os.open()pipe() 返回的文件描述符。若要读取由内建函数 open()popen()fdopen()sys.stdin 返回的 “文件对象”,则应使用其相应的 read()readline() 方法。

在 3.5 版更改: 如果系统调用被中断,但信号处理程序没有触发异常,此函数现在会重试系统调用,而不是触发 InterruptedError 异常 (原因详见 PEP 475)。

os.sendfile(out_fd, in_fd, offset, count)

os.sendfile(out_fd, in_fd, offset, count, headers=(), trailers=(), flags=0)

将文件描述符 in_fd 中的 count 字节复制到文件描述符 out_fd 的偏移位置 offset 处。返回复制的字节数,如果到达 EOF,返回 0

定义了 sendfile() 的所有平台均支持第一种函数用法。

在 Linux 上,将 offset 设置为 None,则从 in_fd 的当前位置开始读取,并更新 in_fd 的位置。

第二种情况可以被用于 macOS 和 FreeBSD,其中 headerstrailers 是任意的缓冲区序列,它们会在写入来自 in_fd 的数据之前被写入。 它的返回内容与第一种情况相同。

在 macOS 和 FreeBSD 上,传入 0 值作为 count 将指定持续发送直至到达 in_fd 的末尾。

所有平台都支持将套接字作为 out_fd 文件描述符,有些平台也支持其他类型(如常规文件或管道)。

跨平台应用程序不应使用 headerstrailersflags 参数。

可用性: Unix。

注解

有关 sendfile() 的高级封装。

3.3 新版功能.

在 3.9 版更改: outin 参数被重命名为 out_fdin_fd

os.set_blocking(fd, blocking)

设置指定文件描述符的阻塞模式:如果 blocking 为 False,则为该描述符设置 O_NONBLOCK 标志位,反之则清除该标志位。

参见 get_blocking()socket.socket.setblocking()

可用性: Unix。

3.5 新版功能.

os.SF_NODISKIO
os.SF_MNOWAIT
os.SF_SYNC

sendfile() 函数的参数(假设当前实现支持这些参数)。

可用性: Unix。

3.3 新版功能.

os.splice(src, dst, count, offset_src=None, offset_dst=None)

由文件描述符 src 传输 count 字节,从偏移量 offset_src 开始读取,到文件描述符 dst*,从偏移量 *offset_dst 开始写入。至少得有一个文件描述符必须指向管道。如果 offset_src 为 None,则 src 将从当前位置开始读取;offset_dst 同理。指向管道的文件描述符,其偏移量必须为 Nonesrcdst 指向的文件必须处于同一文件系统中,否则将会触发 OSError ,其 errno 将被设为 errno.EXDEV

此复制的完成没有额外的从内核到用户空间再回到内核的数据转移花费。另外,一些文件系统可能实现额外的优化。完成复制就如同打开两个二进制文件一样。

调用成功后,返回拼接到管道的字节数或从管道拼接出来的字节数。返回值为 0 意味着输入结束。如果 src 指向一个管道,则意味着没有数据需要传输,而且由于没有写入程序连到管道的写入端,所以将不会阻塞。

可用性: Linux >= 2.6.27 且 glibc >= 2.5。

3.10 新版功能.

os.SPLICE_F_MOVE
os.SPLICE_F_NONBLOCK
os.SPLICE_F_MORE

3.10 新版功能.

os.readv(fd, buffers)

从文件描述符 fd 将数据读取至多个可变的 字节类对象 缓冲区 buffers 中。将数据依次存放到每个缓冲区中,填满一个后继续存放到序列中的下一个缓冲区,来保存其余数据。

返回实际读取的字节总数,该总数可以小于所有对象的总容量。

操作系统可能对允许使用的缓冲区数量有限制(使用 sysconf() 获取 'SC_IOV_MAX' 值)。

可用性: Unix。

3.3 新版功能.

os.tcgetpgrp(fd)

返回与 fd 指定的终端相关联的进程组(fd 是由 os.open() 返回的已打开的文件描述符)。

可用性: Unix。

os.tcsetpgrp(fd, pg)

设置与 fd 指定的终端相关联的进程组为 pg*(*fd 是由 os.open() 返回的已打开的文件描述符)。

可用性: Unix。

os.ttyname(fd)

返回一个字符串,该字符串表示与文件描述符 fd 关联的终端。如果 fd 没有与终端关联,则抛出异常。

可用性: Unix。

os.write(fd, str)

str 中的字节串 (bytestring) 写入文件描述符 fd

返回实际写入的字节数。

注解

该功能适用于低级 I/O 操作,必须用于 os.open()pipe() 返回的文件描述符。若要写入由内建函数 open()popen()fdopen()sys.stdoutsys.stderr 返回的 “文件对象”,则应使用其相应的 write() 方法。

在 3.5 版更改: 如果系统调用被中断,但信号处理程序没有触发异常,此函数现在会重试系统调用,而不是触发 InterruptedError 异常 (原因详见 PEP 475)。

os.writev(fd, buffers)

将缓冲区 buffers 的内容写入文件描述符 fd*。缓冲区 *buffers 必须是由 字节类对象 组成的序列。缓冲区以数组顺序处理。先写入第一个缓冲区的全部内容,再写入第二个缓冲区,照此继续。

返回实际写入的字节总数。

操作系统可能对允许使用的缓冲区数量有限制(使用 sysconf() 获取 'SC_IOV_MAX' 值)。

可用性: Unix。

3.3 新版功能.

查询终端的尺寸

3.3 新版功能.

os.get_terminal_size(fd=STDOUT_FILENO)

返回终端窗口的尺寸,格式为 (columns, lines),它是类型为 terminal_size 的元组。

可选参数 fd (默认为 STDOUT_FILENO 或标准输出)指定应查询的文件描述符。

如果文件描述符未连接到终端,则抛出 OSError 异常。

shutil.get_terminal_size() 是供常规使用的高阶函数,os.get_terminal_size 是其底层的实现。

可用性: Unix, Windows。

class os.terminal_size

元组的子类,存储终端窗口尺寸 (columns, lines)

  • columns

    终端窗口的宽度,单位为字符。

  • lines

    终端窗口的高度,单位为字符。

文件描述符的继承

3.4 新版功能.

每个文件描述符都有一个 “inheritable”(可继承)标志位,该标志位控制了文件描述符是否可以由子进程继承。从 Python 3.4 开始,由 Python 创建的文件描述符默认是不可继承的。

在 UNIX 上,执行新程序时,不可继承的文件描述符在子进程中是关闭的,其他文件描述符将被继承。

在 Windows 上,不可继承的句柄和文件描述符在子进程中是关闭的,但标准流(文件描述符 0、1 和 2 即标准输入、标准输出和标准错误)是始终继承的。如果使用 spawn* 函数,所有可继承的句柄和文件描述符都将被继承。如果使用 subprocess 模块,将关闭除标准流以外的所有文件描述符,并且仅当 close_fds 参数为 False 时才继承可继承的句柄。

os.get_inheritable(fd)

获取指定文件描述符的“可继承”标志位(为布尔值)。

os.set_inheritable(fd, inheritable)

设置指定文件描述符的“可继承”标志位。

os.get_handle_inheritable(handle)

获取指定句柄的“可继承”标志位(为布尔值)。

可用性: Windows。

os.set_handle_inheritable(handle, inheritable)

设置指定句柄的“可继承”标志位。

可用性: Windows。

文件和目录

在某些 Unix 平台上,许多函数支持以下一项或多项功能:

  • 指定文件描述符为参数: 通常在 os 模块中提供给函数的 path 参数必须是表示文件路径的字符串,但是,某些函数现在可以接受其 path 参数为打开文件描述符,该函数将对描述符指向的文件进行操作。(对于 POSIX 系统,Python 将调用以 f 开头的函数变体(如调用 fchdir 而不是 chdir)。)

    可以用 os.supports_fd 检查某个函数在你的平台上是否支持将 path 参数指定为文件描述符。如果不支持,使用该功能将抛出 NotImplementedError 异常。

    如果该函数还支持 dir_fdfollow_symlinks 参数,那么用文件描述符作为 path 后就不能再指定上述参数了。

  • 基于目录描述符的相对路径: 如果 dir_fd 不是 None,它就应该是一个指向目录的文件描述符,这时待操作的 path 应该是相对路径,相对路径是相对于前述目录的。如果 path 是绝对路径,则 dir_fd 将被忽略。(对于 POSIX 系统,Python 将调用该函数的变体,变体以 at 结尾,可能以 f 开头(如调用 faccessat 而不是 access)。

    可以用 os.supports_dir_fd 检查某个函数在你的平台上是否支持 dir_fd。如果不支持,使用该功能将抛出 NotImplementedError 异常。

  • 不跟踪符号链接: 如果 follow_symlinksFalse,并且待操作路径的最后一个元素是符号链接,则该函数将在符号链接本身而不是链接所指向的文件上操作。(对于 POSIX 系统,Python 将调用该函数的 l... 变体。)

    可以用 os.supports_follow_symlinks 检查某个函数在你的平台上是否支持 follow_symlinks。如果不支持,使用该功能将抛出 NotImplementedError 异常。

os.access(path, mode, **, dir_fd=None, effective_ids=False, follow_symlinks=True*)

使用 实际用户ID/用户组ID 测试对 path 的访问。请注意,大多数测试操作将使用 有效用户ID/用户组ID,因此可以在 suid/sgid 环境中运用此例程,来测试调用用户是否具有对 path 的指定访问权限。modeF_OK 时用于测试 path 是否存在,也可以对 R_OKW_OKX_OK 中的一个或多个进行“或”运算来测试指定权限。允许访问则返回 True,否则返回 False

本函数支持指定 基于目录描述符的相对路径 和 不跟踪符号链接。

如果 effective_idsTrueaccess() 将使用 有效用户ID/用户组ID 而非 实际用户ID/用户组ID 进行访问检查。您的平台可能不支持 effective_ids,您可以使用 os.supports_effective_ids 检查它是否可用。如果不可用,使用它时会抛出 NotImplementedError 异常。

注解

使用 access() 来检查用户是否具有某项权限(如打开文件的权限),然后再使用 open() 打开文件,这样做存在一个安全漏洞,因为用户可能会在检查和打开文件之间的时间里做其他操作。推荐使用 EAFP 技术。如:

if os.access("myfile", os.R_OK):
    with open("myfile") as fp:
        return fp.read()
return "some default data"

最好写成:

try:
    fp = open("myfile")
except PermissionError:
    return "some default data"
else:
    with fp:
        return fp.read()

注解

即使 access() 指示 I/O 操作会成功,但实际操作仍可能失败,尤其是对网络文件系统的操作,其权限语义可能超出常规的 POSIX 权限位模型。

在 3.3 版更改: 添加 dir_fdeffective_idsfollow_symlinks 参数。

在 3.6 版更改: 接受一个 path-like object。

os.F_OK
os.R_OK
os.W_OK
os.X_OK

作为 access()mode 参数的可选值,分别测试 path 的存在性、可读性、可写性和可执行性。

os.chdir(path)

将当前工作目录更改为 path

本函数支持 指定文件描述符为参数。其中,描述符必须指向打开的目录,不能是打开的文件。

本函数可以抛出 OSError 及其子类的异常,如 FileNotFoundErrorPermissionErrorNotADirectoryError 异常。

引发一个 审计事件 os.chdir,附带参数 path

3.3 新版功能: 在某些平台上新增支持将 path 参数指定为文件描述符。

在 3.6 版更改: 接受一个 path-like object。

os.chflags(path, flags, **, follow_symlinks=True*)

path 的 flags 设置为其他由数字表示的 flagsflags 可以用以下值按位或组合起来(以下值在 stat 模块中定义):

  • stat.UF_NODUMP
  • stat.UF_IMMUTABLE
  • stat.UF_APPEND
  • stat.UF_OPAQUE
  • stat.UF_NOUNLINK
  • stat.UF_COMPRESSED
  • stat.UF_HIDDEN
  • stat.SF_ARCHIVED
  • stat.SF_IMMUTABLE
  • stat.SF_APPEND
  • stat.SF_NOUNLINK
  • stat.SF_SNAPSHOT

本函数支持 不跟踪符号链接。

引发一个 审计事件 os.chflags,附带参数 pathflags

可用性: Unix。

3.3 新版功能: follow_symlinks 参数。

在 3.6 版更改: 接受一个 path-like object。

os.chmod(path, mode, **, dir_fd=None, follow_symlinks=True*)

path 的 mode 更改为其他由数字表示的 modemode 可以用以下值之一,也可以将它们按位或组合起来(以下值在 stat 模块中定义):

  • stat.S_ISUID
  • stat.S_ISGID
  • stat.S_ENFMT
  • stat.S_ISVTX
  • stat.S_IREAD
  • stat.S_IWRITE
  • stat.S_IEXEC
  • 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

本函数支持 指定文件描述符、指定基于目录描述符的相对路径 和 不跟踪符号链接。

注解

尽管 Windows 支持 chmod(),但只能用它设置文件的只读标志(stat.S_IWRITEstat.S_IREAD 常量或对应的整数值)。所有其他标志位都会被忽略。

引发一个 审计事件 os.chmod,附带参数 pathmodedir_fd

3.3 新版功能: 添加了指定 path 为文件描述符的支持,以及 dir_fdfollow_symlinks 参数。

在 3.6 版更改: 接受一个 path-like object。

os.chown(path, uid, gid, **, dir_fd=None, follow_symlinks=True*)

path 的用户和组 ID 分别修改为数字形式的 uidgid。若要使其中某个 ID 保持不变,请将其置为 -1。

本函数支持 指定文件描述符、指定基于目录描述符的相对路径 和 不跟踪符号链接。

参见更高阶的函数 shutil.chown(),除了数字 ID 之外,它也接受名称。

引发一个 审计事件 os.chown,附带参数 pathuidgiddir_fd

可用性: Unix。

3.3 新版功能: 添加了指定 path 为文件描述符的支持,以及 dir_fdfollow_symlinks 参数。

在 3.6 版更改: 支持 类路径对象。

os.chroot(path)

将当前进程的根目录更改为 path

可用性: Unix。

在 3.6 版更改: 接受一个 path-like object。

os.fchdir(fd)

将当前工作目录更改为文件描述符 fd 指向的目录。fd 必须指向打开的目录而非文件。从 Python 3.3 开始,它等效于 os.chdir(fd)

引发一个 审计事件 os.chdir,附带参数 path

可用性: Unix。

os.getcwd()

返回表示当前工作目录的字符串。

os.getcwdb()

返回表示当前工作目录的字节串 (bytestring)。

在 3.8 版更改: 在 Windows 上,本函数现在会使用 UTF-8 编码格式而不是 ANSI 代码页:请参看 PEP 529 了解具体原因。 该函数在 Windows 上不再被弃用。

os.lchflags(path, flags)

path 的 flags 设置为其他由数字表示的 flags,与 chflags() 类似,但不跟踪符号链接。从 Python 3.3 开始,它等效于 os.chflags(path, flags, follow_symlinks=False)

引发一个 审计事件 os.chflags,附带参数 pathflags

可用性: Unix。

在 3.6 版更改: 接受一个 path-like object。

os.lchmod(path, mode)

path 的权限状态修改为 mode*。如果 path 是符号链接,则影响符号链接本身而非链接目标。可以参考 chmod() 中列出 *mode 的可用值。从 Python 3.3 开始,它等效于 os.chmod(path, mode, follow_symlinks=False)

引发一个 审计事件 os.chmod,附带参数 pathmodedir_fd

可用性: Unix。

在 3.6 版更改: 接受一个 path-like object。

os.lchown(path, uid, gid)

path 的用户和组 ID 分别修改为数字形式的 uidgid,本函数不跟踪符号链接。从 Python 3.3 开始,它等效于 os.chown(path, uid, gid, follow_symlinks=False)

引发一个 审计事件 os.chown,附带参数 pathuidgiddir_fd

可用性: Unix。

在 3.6 版更改: 接受一个 path-like object。

os.link(src, dst, **, src_dir_fd=None, dst_dir_fd=None, follow_symlinks=True*)

创建一个指向 src 的硬链接,名为 dst

本函数支持将 src_dir_fddst_dir_fd 中的一个或两个指定为 基于目录描述符的相对路径,支持 不跟踪符号链接。

引发一个 审计事件 os.link 附带参数 srcdstsrc_dir_fddst_dir_fd

可用性: Unix, Windows。

在 3.2 版更改: 添加了对 Windows 的支持。

3.3 新版功能: 添加 src_dir_fddst_dir_fdfollow_symlinks 参数。

在 3.6 版更改: 接受一个 类路径对象 作为 srcdst

os.listdir(path=’.’)

返回一个包含由 path 指定目录中条目名称组成的列表。 该列表按任意顺序排列,并且不包括特殊条目 '.''..',即使它们存在于目录中。 如果有文件在调用此函数期间在被移除或添加到目录中,是否要包括该文件的名称并没有规定。

path 可以是 类路径对象。如果 path 是(直接传入或通过 PathLike 接口间接传入) bytes 类型,则返回的文件名也将是 bytes 类型,其他情况下是 str 类型。

本函数也支持 指定文件描述符为参数,其中描述符必须指向目录。

引发一个 审计事件 os.listdir,附带参数 path

注解

要将 str 类型的文件名编码为 bytes,请使用 fsencode()

参见

scandir() 函数返回目录内文件名的同时,也返回文件属性信息,它在某些具体情况下能提供更好的性能。

在 3.2 版更改: path 变为可选参数。

3.3 新版功能: 新增支持将 path 参数指定为打开的文件描述符。

在 3.6 版更改: 接受一个 path-like object。

os.lstat(path, **, dir_fd=None*)

在给定路径上执行本函数,其操作相当于 lstat() 系统调用,类似于 stat() 但不跟踪符号链接。返回值是 stat_result 对象。

在不支持符号链接的平台上,本函数是 stat() 的别名。

从 Python 3.3 起,此功能等价于 os.stat(path, dir_fd=dir_fd, follow_symlinks=False)

本函数支持 基于目录描述符的相对路径。

参见

stat() 函数。

在 3.2 版更改: 添加对 Windows 6.0 (Vista) 符号链接的支持。

在 3.3 版更改: 添加了 dir_fd 参数。

在 3.6 版更改: 接受一个 path-like object。

在 3.8 版更改: 目前在 Windows 上,遇到表示另一个路径的重解析点(即名称代理,包括符号链接和目录结点),本函数将打开它。其他种类的重解析点由 stat() 交由操作系统解析。

os.mkdir(path, mode=511, **, dir_fd=None*)

创建一个名为 path 的目录,应用以数字表示的权限模式 mode

如果目录已存在,则抛出 FileExistsError 异常。

某些系统会忽略 mode*。如果没有忽略它,那么将首先从它中减去当前的 umask 值。如果除最后 9 位(即 *mode 八进制的最后 3 位)之外,还设置了其他位,则其他位的含义取决于各个平台。在某些平台上,它们会被忽略,应显式调用 chmod() 进行设置。

本函数支持 基于目录描述符的相对路径。

引发一个 审计事件 os.mkdir,附带参数 pathmodedir_fd

3.3 新版功能: dir_fd 参数。

在 3.6 版更改: 接受一个 path-like object。

os.makedirs(name, mode=511, exist_ok=False)

递归目录创建函数。与 mkdir() 类似,但会自动创建到达最后一级目录所需要的中间目录。

mode 参数会传递给 mkdir(),用来创建最后一级目录,对于该参数的解释。要设置某些新建的父目录的权限,可以在调用 makedirs() 之前设置 umask。现有父目录的权限不会更改。

如果 exist_okFalse (默认值),则如果目标目录已存在将引发 FileExistsError

注解

如果要创建的路径元素包含 pardir (如 UNIX 系统中的 “..”) makedirs() 将无法明确目标。

本函数能正确处理 UNC 路径。

引发一个 审计事件 os.mkdir,附带参数 pathmodedir_fd

3.2 新版功能: exist_ok 参数。

在 3.4.1 版更改: 在 Python 3.4.1 以前,如果 exist_okTrue,且目录已存在,且 mode 与现有目录的权限不匹配,makedirs() 仍会抛出错误。由于无法安全地实现此行为,因此在 Python 3.4.1 中将该行为删除。

在 3.6 版更改: 接受一个 path-like object。

在 3.7 版更改: mode 参数不再影响新创建的中间目录的权限。

os.mkfifo(path, mode=438, **, dir_fd=None*)

创建一个名为 path 的 FIFO(命名管道,一种先进先出队列),具有以数字表示的权限状态 mode。将从 mode 中首先减去当前的 umask 值。

本函数支持 基于目录描述符的相对路径。

FIFO 是可以像常规文件一样访问的管道。FIFO 如果没有被删除(如使用 os.unlink()),会一直存在。通常,FIFO 用作“客户端”和“服务器”进程之间的汇合点:服务器打开 FIFO 进行读取,而客户端打开 FIFO 进行写入。请注意,mkfifo() 不会打开 FIFO —- 它只是创建汇合点。

可用性: Unix。

3.3 新版功能: dir_fd 参数。

在 3.6 版更改: 接受一个 path-like object。

os.mknod(path, mode=384, device=0, **, dir_fd=None*)

创建一个名为 path 的文件系统节点(文件,设备专用文件或命名管道)。mode 指定权限和节点类型,方法是将权限与下列节点类型 stat.S_IFREGstat.S_IFCHRstat.S_IFBLKstat.S_IFIFO 之一(按位或)组合(这些常量可以在 stat 模块中找到)。对于 stat.S_IFCHRstat.S_IFBLKdevice 参数指定了新创建的设备专用文件(可能会用到 os.makedev()),否则该参数将被忽略。

本函数支持 基于目录描述符的相对路径。

可用性: Unix。

3.3 新版功能: dir_fd 参数。

在 3.6 版更改: 接受一个 path-like object。

os.major(device)

提取主设备号,提取自原始设备号(通常是 stat 中的 st_devst_rdev 字段)。

os.minor(device)

提取次设备号,提取自原始设备号(通常是 stat 中的 st_devst_rdev 字段)。

os.makedev(major, minor)

将主设备号和次设备号组合成原始设备号。

os.pathconf(path, name)

返回所给名称的文件有关的系统配置信息。name 指定要查找的配置名称,它可以是字符串,是一个系统已定义的名称,这些名称定义在不同标准(POSIX.1,Unix 95,Unix 98 等)中。一些平台还定义了额外的其他名称。当前操作系统已定义的名称在 pathconf_names 字典中给出。对于未包含在该映射中的配置名称,也可以传递一个整数作为 name

如果 name 是一个字符串且不是已定义的名称,将抛出 ValueError 异常。如果当前系统不支持 name 指定的配置名称,即使该名称存在于 pathconf_names,也会抛出 OSError 异常,错误码为 errno.EINVAL

本函数支持 指定文件描述符为参数。

可用性: Unix。

在 3.6 版更改: 接受一个 path-like object。

os.pathconf_names

字典,表示映射关系,为 pathconf()fpathconf() 可接受名称与操作系统为这些名称定义的整数值之间的映射。这可用于判断系统已定义了哪些名称。

可用性: Unix。

os.readlink(path, **, dir_fd=None*)

返回一个字符串,为符号链接指向的实际路径。其结果可以是绝对或相对路径。如果是相对路径,则可用 os.path.join(os.path.dirname(path), result) 转换为绝对路径。

如果 path 是字符串对象(直接传入或通过 PathLike 接口间接传入),则结果也将是字符串对象,且此类调用可能会引发 UnicodeDecodeError。如果 path 是字节对象(直接传入或间接传入),则结果将会是字节对象。

本函数支持 基于目录描述符的相对路径。

当尝试解析的路径可能含有链接时,请改用 realpath() 以正确处理递归和平台差异。

可用性: Unix, Windows。

在 3.2 版更改: 添加对 Windows 6.0 (Vista) 符号链接的支持。

3.3 新版功能: dir_fd 参数。

在 3.6 版更改: 在 Unix 上可以接受一个 类路径对象。

在 3.8 版更改: 在 Windows 上接受 类路径对象 和字节对象。

在 3.8 版更改: 增加了对目录链接的支持,且返回值改为了“替换路径”的形式(通常带有 \\?\ 前缀),而不是先前那样返回可选的 “print name” 字段。

os.remove(path, **, dir_fd=None*)

移除(删除)文件 path*。 如果 *path 是目录,则会引发 IsADirectoryError。 请使用 rmdir() 来删除目录。 如果文件不存在,则会引发 FileNotFoundError

本函数支持 基于目录描述符的相对路径。

在 Windows 上,尝试删除正在使用的文件会抛出异常。而在 Unix 上,虽然该文件的条目会被删除,但分配给文件的存储空间仍然不可用,直到原始文件不再使用为止。

本函数在语义上与 unlink() 相同。

引发一个 审计事件 os.remove,附带参数 pathdir_fd

3.3 新版功能: dir_fd 参数。

在 3.6 版更改: 接受一个 path-like object。

os.removedirs(name)

递归删除目录。工作方式类似于 rmdir(),不同之处在于,如果成功删除了末尾一级目录,removedirs() 会尝试依次删除 path 中提到的每个父目录,直到抛出错误为止(但该错误会被忽略,因为这通常表示父目录不是空目录)。例如,os.removedirs('foo/bar/baz') 将首先删除目录 'foo/bar/baz',然后如果 'foo/bar''foo' 为空,则继续删除它们。如果无法成功删除末尾一级目录,则抛出 OSError 异常。

引发一个 审计事件 os.remove,附带参数 pathdir_fd

在 3.6 版更改: 接受一个 path-like object。

os.rename(src, dst, **, src_dir_fd=None, dst_dir_fd=None*)

将文件或目录 src 重命名为 dst*。如果 *dst 已存在,则下列情况下将会操作失败,并抛出 OSError 的子类:

在 Windows 上,如果 dst 已存在,则抛出 FileExistsError 异常。

在 Unix 上,如果 src 是文件而 dst 是目录,将抛出 IsADirectoryError 异常,反之则抛出 NotADirectoryError 异常。如果两者都是目录且 dst 为空,则 dst 将被静默替换。如果 dst 是非空目录,则抛出 OSError 异常。如果两者都是文件,则在用户具有权限的情况下,将对 dst 进行静默替换。如果 srcdst 在不同的文件系统上,则本操作在某些 Unix 分支上可能会失败。如果成功,重命名操作将是一个原子操作(这是 POSIX 的要求)。

本函数支持将 src_dir_fddst_dir_fd 中的一个或两个指定为 基于目录描述符的相对路径。

如果需要在不同平台上都能替换目标,请使用 replace()

引发一个 审计事件 os.rename 附带参数 srcdstsrc_dir_fddst_dir_fd

3.3 新版功能: src_dir_fddst_dir_fd 参数。

在 3.6 版更改: 接受一个 类路径对象 作为 srcdst

os.renames(old, new)

递归重命名目录或文件。工作方式类似 rename(),除了会首先创建新路径所需的中间目录。重命名后,将调用 removedirs() 删除旧路径中不需要的目录。

注解

如果用户没有权限删除末级的目录或文件,则本函数可能会无法建立新的目录结构。

引发一个 审计事件 os.rename 附带参数 srcdstsrc_dir_fddst_dir_fd

在 3.6 版更改: 接受一个 类路径对象 作为 oldnew

os.replace(src, dst, **, src_dir_fd=None, dst_dir_fd=None*)

将文件或目录 src 重命名为 dst*。如果 *dst 是目录,将抛出 OSError 异常。如果 dst 已存在且为文件,则在用户具有权限的情况下,将对其进行静默替换。如果 srcdst 在不同的文件系统上,本操作可能会失败。如果成功,重命名操作将是一个原子操作(这是 POSIX 的要求)。

本函数支持将 src_dir_fddst_dir_fd 中的一个或两个指定为 基于目录描述符的相对路径。

引发一个 审计事件 os.rename 附带参数 srcdstsrc_dir_fddst_dir_fd

3.3 新版功能.

在 3.6 版更改: 接受一个 类路径对象 作为 srcdst

os.rmdir(path, **, dir_fd=None*)

移除(删除)目录 path。如果目录不存在或不为空,则会分别抛出 FileNotFoundErrorOSError 异常。要删除整个目录树,可以使用 shutil.rmtree()

本函数支持 基于目录描述符的相对路径。

引发一个 审计事件 os.rmdir,附带参数 pathdir_fd

3.3 新版功能: dir_fd 参数。

在 3.6 版更改: 接受一个 path-like object。

os.scandir(path=’.’)

返回一个 os.DirEntry 对象的迭代器,它们对应于由 path 指定目录中的条目。 这些条目会以任意顺序生成,并且不包括特殊条目 '.''..'。 如果有文件在迭代器创建之后在目录中被移除或添加,是否要包括该文件对应的条目并没有规定。

如果需要文件类型或文件属性信息,使用 scandir() 代替 listdir() 可以大大提高这部分代码的性能,因为如果操作系统在扫描目录时返回的是 os.DirEntry 对象,则该对象包含了这些信息。所有 os.DirEntry 的方法都可能执行一次系统调用,但是 is_dir()is_file() 通常只在有符号链接时才执行一次系统调用。os.DirEntry.stat() 在 Unix 上始终需要一次系统调用,而在 Windows 上只在有符号链接时才需要。

path 可以是 类路径对象。如果 path 是(直接传入或通过 PathLike 接口间接传入的) bytes 类型,那么每个 os.DirEntrynamepath 属性将是 bytes 类型,其他情况下是 str 类型。

本函数也支持 指定文件描述符为参数,其中描述符必须指向目录。

引发一个 审计事件 os.scandir,附带参数 path

scandir() 迭代器支持 上下文管理 协议,并具有以下方法:

  • scandir.close()

    关闭迭代器并释放占用的资源。

    当迭代器迭代完毕,或垃圾回收,或迭代过程出错时,将自动调用本方法。但仍建议显式调用它或使用 with 语句。

    3.6 新版功能.

下面的例子演示了 scandir() 的简单用法,用来显示给定 path 中所有不以 '.' 开头的文件(不包括目录)。entry.is_file() 通常不会增加一次额外的系统调用:

with os.scandir(path) as it:
    for entry in it:
        if not entry.name.startswith('.') and entry.is_file():
            print(entry.name)

注解

在基于 Unix 的系统上,scandir() 使用系统的 opendir() 和 readdir() 函数。在 Windows 上,它使用 Win32 FindFirstFileW 和 FindNextFileW 函数。

3.5 新版功能.

3.6 新版功能: 添加了对 上下文管理 协议和 close() 方法的支持。如果 scandir() 迭代器没有迭代完毕且没有显式关闭,其析构函数将发出 ResourceWarning 警告。

本函数接受一个 类路径对象。

在 3.7 版更改: 在 Unix 上新增支持 指定文件描述符为参数。

class os.DirEntry

scandir() 生成的对象,用于显示目录内某个条目的文件路径和其他文件属性。

scandir() 将在不进行额外系统调用的情况下,提供尽可能多的此类信息。每次进行 stat()lstat() 系统调用时,os.DirEntry 对象会将结果缓存下来。

os.DirEntry 实例不适合存储在长期存在的数据结构中,如果你知道文件元数据已更改,或者自调用 scandir() 以来已经经过了很长时间,请调用 os.stat(entry.path) 来获取最新信息。

因为 os.DirEntry 方法可以进行系统调用,所以它也可能抛出 OSError 异常。如需精确定位错误,可以逐个调用 os.DirEntry 中的方法来捕获 OSError,并适当处理。

为了能直接用作 类路径对象,os.DirEntry 实现了 PathLike 接口。

os.DirEntry 实例所包含的属性和方法如下:

  • name

    本条目的基本文件名,是根据 scandir()path 参数得出的相对路径。

    如果 scandir()path 参数是 bytes 类型,则 name 属性也是 bytes 类型,否则为 str。使用 fsdecode() 解码 byte 类型的文件名。

  • path

    本条目的完整路径:等效于 os.path.join(scandir_path, entry.name),其中 scandir_path 就是 scandir()path 参数。仅当 scandir()path 参数为绝对路径时,本路径才是绝对路径。如果 scandir()path 参数是 文件描述符,则 path 属性与上述 name 属性相同。

    如果 scandir()path 参数是 bytes 类型,则 path 属性也是 bytes 类型,否则为 str。使用 fsdecode() 解码 byte 类型的文件名。

  • inode()

    返回本条目的索引节点号 (inode number)。

    这一结果是缓存在 os.DirEntry 对象中的,请调用 os.stat(entry.path, follow_symlinks=False).st_ino 来获取最新信息。

    一开始没有缓存时,在 Windows 上需要一次系统调用,但在 Unix 上不需要。

  • is_dir(**, follow_symlinks=True*)

    如果本条目是目录,或是指向目录的符号链接,则返回 True。如果本条目是文件,或指向任何其他类型的文件,或该目录不再存在,则返回 False

    如果 follow_symlinksFalse,那么仅当本条目为目录时返回 True (不跟踪符号链接),如果本条目是任何类型的文件,或该文件不再存在,则返回 False

    这一结果是缓存在 os.DirEntry 对象中的,且 follow_symlinksTrueFalse 时的缓存是分开的。请调用 os.stat()stat.S_ISDIR() 来获取最新信息。

    一开始没有缓存时,大多数情况下不需要系统调用。特别是对于非符号链接,Windows 和 Unix 都不需要系统调用,除非某些 Unix 文件系统(如网络文件系统)返回了 dirent.d_type == DT_UNKNOWN。如果本条目是符号链接,则需要一次系统调用来跟踪它(除非 follow_symlinksFalse)。

    本方法可能抛出 OSError 异常,如 PermissionError 异常,但 FileNotFoundError 异常会被内部捕获且不会抛出。

  • is_file(**, follow_symlinks=True*)

    如果本条目是文件,或是指向文件的符号链接,则返回 True。如果本条目是目录,或指向目录,或指向其他非文件条目,或该文件不再存在,则返回 False

    如果 follow_symlinksFalse,那么仅当本条目为文件时返回 True (不跟踪符号链接),如果本条目是目录或其他非文件条目,或该文件不再存在,则返回 False

    这一结果是缓存在 os.DirEntry 对象中的。缓存、系统调用、异常抛出都与 is_dir() 一致。

  • is_symlink()

    如果本条目是符号链接(即使是断开的链接),返回 True。如果是目录或任何类型的文件,或本条目不再存在,返回 False

    这一结果是缓存在 os.DirEntry 对象中的,请调用 os.path.islink() 来获取最新信息。

    一开始没有缓存时,大多数情况下不需要系统调用。其实 Windows 和 Unix 都不需要系统调用,除非某些 Unix 文件系统(如网络文件系统)返回了 dirent.d_type == DT_UNKNOWN

    本方法可能抛出 OSError 异常,如 PermissionError 异常,但 FileNotFoundError 异常会被内部捕获且不会抛出。

  • stat(**, follow_symlinks=True*)

    返回本条目对应的 stat_result 对象。本方法默认会跟踪符号链接,要获取符号链接本身的 stat,请添加 follow_symlinks=False 参数。

    在 Unix 上,本方法需要一次系统调用。在 Windows 上,仅在 follow_symlinksTrue 且该条目是一个重解析点(如符号链接或目录结点)时,才需要一次系统调用。

    在 Windows 上,stat_resultst_inost_devst_nlink 属性总是为零。请调用 os.stat() 以获得这些属性。

    这一结果是缓存在 os.DirEntry 对象中的,且 follow_symlinksTrueFalse 时的缓存是分开的。请调用 os.stat() 来获取最新信息。

注意,os.DirEntrypathlib.Path 的几个属性和方法之间存在很好的对应关系。具体来说是 name 属性,以及 is_dir()is_file()is_symlink()stat() 方法,在两个类中具有相同的含义。

3.5 新版功能.

在 3.6 版更改: 添加了对 PathLike 接口的支持。在 Windows 上添加了对 bytes 类型路径的支持。

os.stat(path, **, dir_fd=None, follow_symlinks=True*)

获取文件或文件描述符的状态。在所给路径上执行等效于 stat() 系统调用的操作。path 可以是字符串类型,或(直接传入或通过 PathLike 接口间接传入的) bytes 类型,或打开的文件描述符。返回一个 stat_result 对象。

本函数默认会跟踪符号链接,要获取符号链接本身的 stat,请添加 follow_symlinks=False 参数,或使用 lstat()

本函数支持 指定文件描述符为参数 和 不跟踪符号链接。

在 Windows 上,传入 follow_symlinks=False 将禁用所有名称代理重解析点,其中包括符号链接和目录结点。其他类型的重解析点将直接打开,比如不像链接的或系统无法跟踪的重解析点。当多个链接形成一个链时,本方法可能会返回原始链接的 stat,无法完整遍历到非链接的对象。在这种情况下,要获取最终路径的 stat,请使用 os.path.realpath() 函数尽可能地解析路径,并在解析结果上调用 lstat()。这不适用于空链接或交接点,否则会抛出异常。

示例:

>>> import os
>>> statinfo = os.stat('somefile.txt')
>>> statinfo
os.stat_result(st_mode=33188, st_ino=7876932, st_dev=234881026,
st_nlink=1, st_uid=501, st_gid=501, st_size=264, st_atime=1297230295,
st_mtime=1297230027, st_ctime=1297230027)
>>> statinfo.st_size
264

参见

fstat()lstat() 函数。

3.3 新版功能: 增加 dir_fdfollow_symlinks 参数,可指定文件描述符代替路径。

在 3.6 版更改: 接受一个 path-like object。

在 3.8 版更改: 在 Windows 上,本方法将跟踪系统能解析的所有重解析点,并且传入 follow_symlinks=False 会停止跟踪所有名称代理重解析点。现在,如果操作系统遇到无法跟踪的重解析点,stat 将返回原始路径的信息,就像已指定 follow_symlinks=False 一样,而不会抛出异常。

class os.stat_result

本对象的属性大致对应于 stat 结构体成员,主要作为 os.stat()os.fstat()os.lstat() 的返回值。

属性:

  • st_mode

    文件模式:包括文件类型和文件模式位(即权限位)。

  • st_ino

    与平台有关,但如果不为零,则根据 st_dev 值唯一地标识文件。通常:

    • 在 Unix 上该值表示索引节点号 (inode number)。
    • 在 Windows 上该值表示 文件索引号 。
  • st_dev

    该文件所在设备的标识符。

  • st_nlink

    硬链接的数量。

  • st_uid

    文件所有者的用户 ID。

  • st_gid

    文件所有者的用户组 ID。

  • st_size

    文件大小(以字节为单位),文件可以是常规文件或符号链接。符号链接的大小是它包含的路径的长度,不包括末尾的空字节。

时间戳:

  • st_atime

    最近的访问时间,以秒为单位。

  • st_mtime

    最近的修改时间,以秒为单位。

  • st_ctime

    取决于平台:

    • 在 Unix 上表示最近的元数据更改时间,
    • 在 Windows 上表示创建时间,以秒为单位。
  • st_atime_ns

    最近的访问时间,以纳秒表示,为整数。

  • st_mtime_ns

    最近的修改时间,以纳秒表示,为整数。

  • st_ctime_ns

    取决于平台:

    • 在 Unix 上表示最近的元数据更改时间,
    • 在 Windows 上表示创建时间,以纳秒表示,为整数。

注解

st_atimest_mtimest_ctime 属性的确切含义和分辨率取决于操作系统和文件系统。例如,在使用 FAT 或 FAT32 文件系统的 Windows 上,st_mtime 有 2 秒的分辨率,而 st_atime 仅有 1 天的分辨率。详细信息请参阅操作系统文档。

类似地,尽管 st_atime_nsst_mtime_nsst_ctime_ns 始终以纳秒表示,但许多系统并不提供纳秒精度。在确实提供纳秒精度的系统上,用于存储 st_atimest_mtimest_ctime 的浮点对象无法保留所有精度,因此不够精确。如果需要确切的时间戳,则应始终使用 st_atime_nsst_mtime_nsst_ctime_ns

在某些 Unix 系统上(如 Linux 上),以下属性可能也可用:

  • st_blocks

    为文件分配的字节块数,每块 512 字节。文件是稀疏文件时,它可能小于 st_size/512。

  • st_blksize

    “首选的” 块大小,用于提高文件系统 I/O 效率。写入文件时块大小太小可能会导致读取-修改-重写效率低下。

  • st_rdev

    设备类型(如果是 inode 设备)。

  • st_flags

    用户定义的文件标志位。

在其他 Unix 系统上(如 FreeBSD 上),以下属性可能可用(但仅当 root 使用它们时才被填充):

  • st_gen

    文件生成号。

  • st_birthtime

    文件创建时间。

在 Solaris 及其衍生版本上,以下属性可能也可用:

  • st_fstype

    文件所在文件系统的类型的唯一标识,为字符串。

On macOS systems, the following attributes may also be available:

  • st_rsize

    文件的实际大小。

  • st_creator

    文件的创建者。

  • st_type

    文件类型。

在 Windows 系统上,以下属性也可用:

  • st_file_attributes

    Windows 文件属性:dwFileAttributes,由 GetFileInformationByHandle() 返回的 BY_HANDLE_FILE_INFORMATION 结构体的成员之一。

  • st_reparse_tag

    st_file_attributes 存在 FILE_ATTRIBUTE_REPARSE_POINT 集合时,本字段包含重解析点类型标记。

标准模块 stat 中定义了函数和常量,这些函数和常量可用于从 stat 结构体中提取信息。(在 Windows 上,某些项填充的是虚值。)

为了向后兼容,一个 stat_result 实例还可以作为至少包含 10 个整数的元组访问,以提供 stat 结构中最重要(和可移植)的成员,整数顺序为 st_mode, st_ino, st_dev, st_nlink, st_uid, st_gid, st_size, st_atime, st_mtime, st_ctime。某些实现可能在末尾还有更多项。为了与旧版 Python 兼容,以元组形式访问 stat_result 始终返回整数。

3.3 新版功能: 添加了 st_atime_nsst_mtime_nsst_ctime_ns 成员。

3.5 新版功能: 在 Windows 上添加了 st_file_attributes 成员。

在 3.5 版更改: 在 Windows 上,如果可用,会返回文件索引作为 st_ino 的值。

3.7 新版功能: 在 Solaris 及其衍生版本上添加了 st_fstype 成员。

3.8 新版功能: 在 Windows 上添加了 st_reparse_tag 成员。

在 3.8 版更改: 在 Windows 上,st_mode 成员现在可以根据需要将特殊文件标识为 S_IFCHRS_IFIFOS_IFBLK

os.statvfs(path)

在所给的路径上执行 statvfs() 系统调用。返回值是一个对象,其属性描述了所给路径上的文件系统,并且与 statvfs 结构体的成员相对应,即:f_bsize, f_frsize, f_blocks, f_bfree, f_bavail, f_files, f_ffree, f_favail, f_flag, f_namemax, f_fsid

f_flag 属性位定义了两个模块级常量:如果存在 ST_RDONLY 位,则文件系统以只读挂载;如果存在 ST_NOSUID 位,则文件系统禁用或不支持 setuid/setgid 位。

为基于 GNU/glibc 的系统还定义了额外的模块级常量。它们是 ST_NODEV (禁止访问设备专用文件),ST_NOEXEC (禁止执行程序),ST_SYNCHRONOUS (写入后立即同步),ST_MANDLOCK (允许文件系统上的强制锁定),ST_WRITE (写入文件/目录/符号链接),ST_APPEND (仅追加文件),ST_IMMUTABLE (不可变文件),ST_NOATIME (不更新访问时间),ST_NODIRATIME (不更新目录访问时间),ST_RELATIME (相对于 mtime/ctime 更新访问时间)。

本函数支持 指定文件描述符为参数。

可用性: Unix。

在 3.2 版更改: 添加了 ST_RDONLYST_NOSUID 常量。

3.3 新版功能: 新增支持将 path 参数指定为打开的文件描述符。

在 3.4 版更改: 添加了 ST_NODEVST_NOEXECST_SYNCHRONOUSST_MANDLOCKST_WRITEST_APPENDST_IMMUTABLEST_NOATIMEST_NODIRATIMEST_RELATIME 常量。

在 3.6 版更改: 接受一个 path-like object。

3.7 新版功能: 添加了 f_fsid

os.supports_dir_fd

一个 set 对象,指示 os 模块中的哪些函数接受一个打开的文件描述符作为 dir_fd 参数。不同平台提供的功能不同,且 Python 用于实现 dir_fd 参数的底层函数并非在 Python 支持的所有平台上都可用。考虑到一致性,支持 dir_fd 的函数始终允许指定描述符,但如果在底层不支持时调用了该函数,则会抛出异常。(在所有平台上始终支持将 dir_fd 指定为 None。)

要检查某个函数是否接受打开的文件描述符作为 dir_fd 参数,请在 supports_dir_fd 前使用 in 运算符。例如,如果 os.stat() 在当前平台上接受打开的文件描述符作为 dir_fd 参数,则此表达式的计算结果为 True:

os.stat in os.supports_dir_fd

目前 dir_fd 参数仅在 Unix 平台上有效,在 Windows 上均无效。

3.3 新版功能.

os.supports_effective_ids

一个 set 对象,指示 os.access() 是否允许在当前平台上将其 effective_ids 参数指定为 True。(所有平台都支持将 effective_ids 指定为 False。)如果当前平台支持,则集合将包含 os.access(),否则集合为空。

如果当前平台上的 os.access() 支持 effective_ids=True,则此表达式的计算结果为 True:

os.access in os.supports_effective_ids

目前仅 Unix 平台支持 effective_ids,Windows 不支持。

3.3 新版功能.

os.supports_fd

一个 set 对象,指示在当前平台上 os 模块中的哪些函数接受一个打开的文件描述符作为 path 参数。不同平台提供的功能不同,且 Python 所使用到的底层函数(用于实现接受描述符作为 path)并非在 Python 支持的所有平台上都可用。

要判断某个函数是否接受打开的文件描述符作为 path 参数,请在 supports_fd 前使用 in 运算符。例如,如果 os.chdir() 在当前平台上接受打开的文件描述符作为 path 参数,则此表达式的计算结果为 True:

os.chdir in os.supports_fd

3.3 新版功能.

os.supports_follow_symlinks

一个 set 对象,指示在当前平台上 os 模块中的哪些函数的 follow_symlinks 参数可指定为 False。不同平台提供的功能不同,且 Python 用于实现 follow_symlinks 的底层函数并非在 Python 支持的所有平台上都可用。考虑到一致性,支持 follow_symlinks 的函数始终允许将其指定为 False,但如果在底层不支持时调用了该函数,则会抛出异常。(在所有平台上始终支持将 follow_symlinks 指定为 True。)

要检查某个函数的 follow_symlinks 参数是否可以指定为 False,请在 supports_follow_symlinks 前使用 in 运算符。例如,如果在当前平台上调用 os.stat() 时可以指定 follow_symlinks=False,则此表达式的计算结果为 True:

os.stat in os.supports_follow_symlinks

3.3 新版功能.

os.symlink(src, dst, target_is_directory=False, **, dir_fd=None*)

创建一个指向 src 的符号链接,名为 dst

在 Windows 上,符号链接可以表示文件或目录两种类型,并且不会动态改变类型。如果目标存在,则新建链接的类型将与目标一致。否则,如果 target_is_directoryTrue,则符号链接将创建为目录链接,为 False (默认)将创建为文件链接。在非 Windows 平台上,target_is_directory 被忽略。

本函数支持 基于目录描述符的相对路径。

注解

在 Windows 10 或更高版本上,如果启用了开发人员模式,非特权帐户可以创建符号链接。如果开发人员模式不可用/未启用,则需要 SeCreateSymbolicLinkPrivilege 权限,或者该进程必须以管理员身份运行。

当本函数由非特权账户调用时,抛出 OSError 异常。

引发一个 审计事件 os.symlink,附带参数 srcdstdir_fd

可用性: Unix, Windows。

在 3.2 版更改: 添加对 Windows 6.0 (Vista) 符号链接的支持。

3.3 新版功能: 添加了 dir_fd 参数,现在在非 Windows 平台上允许 target_is_directory 参数。

在 3.6 版更改: 接受一个 类路径对象 作为 srcdst

在 3.8 版更改: 针对启用了开发人员模式的 Windows,添加了非特权账户创建符号链接的支持。

os.sync()

强制将所有内容写入磁盘。

可用性: Unix。

3.3 新版功能.

os.truncate(path, length)

截断 path 对应的文件,以使其最大为 length 字节。

本函数支持 指定文件描述符为参数。

引发一个 审计事件 os.truncate,附带参数 path, length

可用性: Unix, Windows。

3.3 新版功能.

在 3.5 版更改: 添加了 Windows 支持

在 3.6 版更改: 接受一个 path-like object。

os.unlink(path, **, dir_fd=None*)

移除(删除)文件 path。该函数在语义上与 remove() 相同,unlink 是其传统的 Unix 名称。

引发一个 审计事件 os.remove,附带参数 pathdir_fd

3.3 新版功能: dir_fd 参数。

在 3.6 版更改: 接受一个 path-like object。

os.utime(path, times=None, *, [ns, ]dir_fd=None,* follow_symlinks=True*)

设置文件 path 的访问时间和修改时间。

utime()timesns 两个可选参数,它们指定了设置给 path 的时间,用法如下:

  • 如果指定 ns,它必须是一个 (atime_ns, mtime_ns) 形式的二元组,其中每个成员都是一个表示纳秒的整数。
  • 如果 times 不为 None,则它必须是 (atime, mtime) 形式的二元组,其中每个成员都是一个表示秒的 int 或 float。
  • 如果 timesNone 且未指定 ns,则相当于指定 ns=(atime_ns, mtime_ns),其中两个时间均为当前时间。

同时为 timesns 指定元组会出错。

注意,根据操作系统记录访问时间和修改时间的分辨率,后续的 stat() 调用可能不会返回此处设置的确切时间。保留精确时间的最佳方法是使用 os.stat() 结果对象中的 st_atime_nsst_mtime_ns 字段,并将 ns 参数设置为 utime。

本函数支持 指定文件描述符、指定基于目录描述符的相对路径 和 不跟踪符号链接。

引发一个 审计事件 os.utime,附带参数 pathtimesnsdir_fd

3.3 新版功能: 新增支持将 path 参数指定为打开的文件描述符,以及支持 dir_fdfollow_symlinksns 参数。

在 3.6 版更改: 接受一个 path-like object。

os.walk(top, topdown=True, onerror=None, followlinks=False)

生成目录树中的文件名,方式是按上->下或下->上顺序浏览目录树。对于以 top 为根的目录树中的每个目录(包括 top 本身),它都会生成一个三元组 (dirpath, dirnames, filenames)

dirpath 是表示目录路径的字符串。 dirnamesdirpath 中子目录名称组成的列表 (excluding '.' and '..')。 filenamesdirpath 中非目录文件名称组成的列表。 请注意列表中的名称不带路径部分。 要获取 dirpath 中文件或目录的完整路径(以 top 打头),请执行 os.path.join(dirpath, name)。 列表是否排序取决于具体文件系统。 如果有文件或列表生成期间被移除或添加到 dirpath 目录中,是否要包括该文件的名称并没有规定。

如果可选参数 topdownTrue 或未指定,则在所有子目录的三元组之前生成父目录的三元组(目录是自上而下生成的)。如果 topdownFalse,则在所有子目录的三元组生成之后再生成父目录的三元组(目录是自下而上生成的)。无论 topdown 为何值,在生成目录及其子目录的元组之前,都将检索全部子目录列表。

topdownTrue 时,调用者可以就地修改 dirnames 列表(也许用到了 del 或切片),而 walk() 将仅仅递归到仍保留在 dirnames 中的子目录内。这可用于减少搜索、加入特定的访问顺序,甚至可在继续 walk() 之前告知 walk() 由调用者新建或重命名的目录的信息。当 topdownFalse 时,修改 dirnames 对 walk 的行为没有影响,因为在自下而上模式中,dirnames 中的目录是在 dirpath 本身之前生成的。

默认将忽略 scandir() 调用中的错误。如果指定了可选参数 onerror,它应该是一个函数。出错时它会被调用,参数是一个 OSError 实例。它可以报告错误然后继续遍历,或者抛出异常然后中止遍历。注意,可以从异常对象的 filename 属性中获取出错的文件名。

walk() 默认不会递归进指向目录的符号链接。可以在支持符号链接的系统上将 followlinks 设置为 True,以访问符号链接指向的目录。

注解

注意,如果链接指向自身的父目录,则将 followlinks 设置为 True 可能导致无限递归。walk() 不会记录它已经访问过的目录。

注解

如果传入的是相对路径,请不要在恢复 walk() 之间更改当前工作目录。walk() 不会更改当前目录,并假定其调用者也不会更改当前目录。

下面的示例遍历起始目录内所有子目录,打印每个目录内的文件占用的字节数,CVS 子目录不会被遍历:

import os
from os.path import join, getsize
for root, dirs, files in os.walk('python/Lib/email'):
    print(root, "consumes", end=" ")
    print(sum(getsize(join(root, name)) for name in files), end=" ")
    print("bytes in", len(files), "non-directory files")
    if 'CVS' in dirs:
        dirs.remove('CVS')  # don't visit CVS directories

在下一个示例(shutil.rmtree() 的简单实现)中,必须使树自下而上遍历,因为 rmdir() 只允许在目录为空时删除目录:

# Delete everything reachable from the directory named in "top",
# assuming there are no symbolic links.
# CAUTION:  This is dangerous!  For example, if top == '/', it
# could delete all your disk files.
import os
for root, dirs, files in os.walk(top, topdown=False):
    for name in files:
        os.remove(os.path.join(root, name))
    for name in dirs:
        os.rmdir(os.path.join(root, name))

引发一个 审计事件 os.walk,附带参数 top, topdown, onerror, followlinks

在 3.5 版更改: 现在,本函数调用的是 os.scandir() 而不是 os.listdir(),从而减少了调用 os.stat() 的次数而变得更快。

在 3.6 版更改: 接受一个 path-like object。

os.fwalk(top=’.’, topdown=True, onerror=None, **, follow_symlinks=False, dir_fd=None*)

本方法的行为与 walk() 完全一样,除了它产生的是 4 元组 (dirpath, dirnames, filenames, dirfd),并且它支持 dir_fd

dirpathdirnamesfilenameswalk() 输出的相同,dirfd 是指向目录 dirpath 的文件描述符。

本函数始终支持 基于目录描述符的相对路径 和 不跟踪符号链接。但是请注意,与其他函数不同,fwalk()follow_symlinks 的默认值为 False

注解

由于 fwalk() 会生成文件描述符,而它们仅在下一个迭代步骤前有效,因此如果要将描述符保留更久,则应复制它们(比如使用 dup())。

下面的示例遍历起始目录内所有子目录,打印每个目录内的文件占用的字节数,CVS 子目录不会被遍历:

import os
for root, dirs, files, rootfd in os.fwalk('python/Lib/email'):
    print(root, "consumes", end="")
    print(sum([os.stat(name, dir_fd=rootfd).st_size for name in files]),
          end="")
    print("bytes in", len(files), "non-directory files")
    if 'CVS' in dirs:
        dirs.remove('CVS')  # don't visit CVS directories

在下一个示例中,必须使树自下而上遍历,因为 rmdir() 只允许在目录为空时删除目录:

# Delete everything reachable from the directory named in "top",
# assuming there are no symbolic links.
# CAUTION:  This is dangerous!  For example, if top == '/', it
# could delete all your disk files.
import os
for root, dirs, files, rootfd in os.fwalk(top, topdown=False):
    for name in files:
        os.unlink(name, dir_fd=rootfd)
    for name in dirs:
        os.rmdir(name, dir_fd=rootfd)

引发一个 审计事件 os.fwalk,附带参数 top, topdown, onerror, follow_symlinks, dir_fd

可用性: Unix。

3.3 新版功能.

在 3.6 版更改: 接受一个 path-like object。

在 3.7 版更改: 添加了对 bytes 类型路径的支持。

os.memfd_create(name[, flags=os.MFD_CLOEXEC])

创建一个匿名文件,返回指向该文件的文件描述符。flags 必须是系统上可用的 os.MFD_* 常量之一(或将它们按位“或”组合起来)。新文件描述符默认是 不可继承的。

name 提供的名称会被用作文件名,并且 /proc/self/fd/ 目录中相应符号链接的目标将显示为该名称。显示的名称始终以 memfd: 为前缀,并且仅用于调试目的。名称不会影响文件描述符的行为,因此多个文件可以有相同的名称,不会有副作用。

可用性:Linux 3.17 或更高版本,且装有 glibc 2.27 或更高版本。

3.8 新版功能.

os.MFD_CLOEXEC
os.MFD_ALLOW_SEALING
os.MFD_HUGETLB
os.MFD_HUGE_SHIFT
os.MFD_HUGE_MASK
os.MFD_HUGE_64KB
os.MFD_HUGE_512KB
os.MFD_HUGE_1MB
os.MFD_HUGE_2MB
os.MFD_HUGE_8MB
os.MFD_HUGE_16MB
os.MFD_HUGE_32MB
os.MFD_HUGE_256MB
os.MFD_HUGE_512MB
os.MFD_HUGE_1GB
os.MFD_HUGE_2GB
os.MFD_HUGE_16GB

以上标志位可以传递给 memfd_create()

可用性:Linux 3.17 或更高版本,且装有 glibc 2.27 或更高版本。MFD_HUGE* 标志仅在 Linux 4.14 及以上可用。

3.8 新版功能.

os.eventfd(initval[, flags=os.EFD_CLOEXEC])

创建并返回一个事件文件描述符。此文件描述符支持缓冲区大小为 8 的原生 read()write() 操作、select()poll() 等类似操作。默认情况下,新的文件描述符是 non-inheritable。

initval 是事件计数器的初始值。初始值必须是一个 32 位无符号整数。请注意,虽然事件计数器是一个无符号的 64 位整数,其最大值为 2 64-2,但初始值仍被限制为 32 位无符号整数。

flags 可由 EFD_CLOEXECEFD_NONBLOCKEFD_SEMAPHORE 组合而成。

如果设置了 EFD_SEMAPHORE,并且事件计数器非零,那么 eventfd_read() 将返回 1 并将计数器递减 1。

如果未设置 EFD_SEMAPHORE,并且事件计数器非零,那么 eventfd_read() 返回当前的事件计数器值,并将计数器重置为零。

如果事件计数器为 0,并且未设置 EFD_NONBLOCK,那么 eventfd_read() 会阻塞。

eventfd_write() 会递增事件计数器。如果写操作会让计数器的增量大于264-2,则写入会被阻止。

示例:

import os
# semaphore with start value '1'
fd = os.eventfd(1, os.EFD_SEMAPHORE | os.EFC_CLOEXEC)
try:
    # acquire semaphore
    v = os.eventfd_read(fd)
    try:
        do_work()
    finally:
        # release semaphore
        os.eventfd_write(fd, v)
finally:
    os.close(fd)

可用性 :Linux 2.6.27 以上版本,带有 glibc 2.8 以上版本。

3.10 新版功能.

os.eventfd_read(fd)

从一个 eventfd() 文件描述符中读取数据,并返回一个 64 位无符号整数。该函数不会校验 fd 是否为 eventfd()

可用性 :参见 eventfd()

3.10 新版功能.

os.eventfd_write(fd, value)

向一个 eventfd() 文件描述符加入数据。value 必须是一个 64 位无符号整数。本函数不会校验 fd 是否为 eventfd()

可用性 :参见 eventfd()

3.10 新版功能.

os.EFD_CLOEXEC

为新的 eventfd() 文件描述符设置 close-on-exec 标志。

可用性 :参见 eventfd()

3.10 新版功能.

os.EFD_NONBLOCK

为新的 eventfd() 文件描述符设置 O_NONBLOCK 状态标志。

可用性 :参见 eventfd()

3.10 新版功能.

os.EFD_SEMAPHORE

为读取 eventfd() 文件描述符的操作提供类似信号量的控制。在读取时,内部计数器递减 1。

可用性: Linux 2.6.30 以上版本,带有 glibc 2.8 以上版本。

3.10 新版功能.

Linux 扩展属性

3.3 新版功能.

这些函数仅在 Linux 上可用。

os.getxattr(path, attribute, **, follow_symlinks=True*)

返回 path 的扩展文件系统属性 attribute 的值。attribute 可以是 bytes 或 str (直接传入或通过 PathLike 接口间接传入)。如果是 str,则使用文件系统编码来编码字符串。

本函数支持 指定文件描述符为参数 和 不跟踪符号链接。

引发一个 审计事件 os.getxattr,附带参数 pathattribute

在 3.6 版更改: 接受一个 类路径对象 作为 pathattribute

os.listxattr(path=None, **, follow_symlinks=True*)

返回一个列表,包含 path 的所有扩展文件系统属性。列表中的属性都表示为字符串,它们是根据文件系统编码解码出来的。如果 pathNone,则 listxattr() 将检查当前目录。

本函数支持 指定文件描述符为参数 和 不跟踪符号链接。

引发一个 审计事件 os.listxattr,附带参数 path

在 3.6 版更改: 接受一个 path-like object。

os.removexattr(path, attribute, **, follow_symlinks=True*)

移除 path 中的扩展文件系统属性 attribute*。 *attribute 应为字节串或字符串类型(通过 PathLike 接口直接或间接得到)。 若为字符串类型,则用 filesystem encoding and error handler 进行编码。

本函数支持 指定文件描述符为参数 和 不跟踪符号链接。

引发一个 审计事件 os.removexattr,附带参数 pathattribute

在 3.6 版更改: 接受一个 类路径对象 作为 pathattribute

os.setxattr(path, attribute, value, flags=0, **, follow_symlinks=True*)

path 的文件系统扩展属性 attribute 设为 value*。 *attribute 必须是一个字节串或字符串,不含 NUL(通过 PathLike 接口直接或间接得到)。 若为字符串,将用 filesystem encoding and error handler 进行编码。 flags 可以是 XATTR_REPLACEXATTR_CREATE。 如果给出 XATTR_REPLACE 而属性不存在,则会触发 ENODATA。 如果给出了 XATTR_CREATE 而属性已存在,则不会创建属性并将触发 EEXISTS

本函数支持 指定文件描述符为参数 和 不跟踪符号链接。

注解

Linux kernel 2.6.39 以下版本的一个 bug 导致在某些文件系统上,flags 参数会被忽略。

引发一个 审计事件 os.setxattr,附带参数 pathattributevalueflags

在 3.6 版更改: 接受一个 类路径对象 作为 pathattribute

os.XATTR_SIZE_MAX

一条扩展属性的值的最大大小。在当前的 Linux 上是 64 KiB。

os.XATTR_CREATE

这是 setxattr() 的 flags 参数的可取值,它表示该操作必须创建一个属性。

os.XATTR_REPLACE

这是 setxattr() 的 flags 参数的可取值,它表示该操作必须替换现有属性。

进程管理

下列函数可用于创建和管理进程。

所有 exec* 函数都接受一个参数列表,用来给新程序加载到它的进程中。在所有情况下,传递给新程序的第一个参数是程序本身的名称,而不是用户在命令行上输入的参数。对于 C 程序员来说,这就是传递给 main() 函数的 argv[0]。例如,os.execv('/bin/echo', ['foo', 'bar']) 只会在标准输出上打印 bar,而 foo 会被忽略。

os.abort()

发送 SIGABRT 信号到当前进程。在 Unix 上,默认行为是生成一个核心转储。在 Windows 上,该进程立即返回退出代码 3。请注意,使用 signal.signal() 可以为 SIGABRT 注册 Python 信号处理程序,而调用本函数将不会调用按前述方法注册的程序。

os.add_dll_directory(path)

将路径添加到 DLL 搜索路径。

当需要解析扩展模块的依赖时(扩展模块本身通过 sys.path 解析),会使用该搜索路径,ctypes 也会使用该搜索路径。

要移除目录,可以在返回的对象上调用 close(),也可以在 with 语句内使用本方法。

参阅 Microsoft 文档 获取如何加载 DLL 的信息。

引发一个 审计事件 os.add_dll_directory,附带参数 path

可用性: Windows。

3.8 新版功能: 早期版本的 CPython 解析 DLL 时用的是当前进程的默认行为。这会导致不一致,比如不是每次都会去搜索 PATH 和当前工作目录,且系统函数(如 AddDllDirectory )失效。

在 3.8 中,DLL 的两种主要加载方式现在可以显式覆盖进程的行为,以确保一致性。

os.execl(path, arg0, arg1, )

os.execle(path, arg0, arg1, , env)

os.execlp(file, arg0, arg1, )

os.execlpe(file, arg0, arg1, , env)

os.execv(path, args)

os.execve(path, args, env)

os.execvp(file, args)

os.execvpe(file, args, env)

这些函数都将执行一个新程序,以替换当前进程。它们没有返回值。在 Unix 上,新程序会加载到当前进程中,且进程号与调用者相同。过程中的错误会被报告为 OSError 异常。

当前进程会被立即替换。打开的文件对象和描述符都不会刷新,因此如果这些文件上可能缓冲了数据,则应在调用 exec* 函数之前使用 sys.stdout.flush()os.fsync() 刷新它们。

exec* 函数的 “l” 和 “v” 变体不同在于命令行参数的传递方式。如果在编码时固定了参数数量,则 “l” 变体可能是最方便的,各参数作为 execl*() 函数的附加参数传入即可。当参数数量可变时,”v” 变体更方便,参数以列表或元组的形式作为 args 参数传递。在这两种情况下,子进程的第一个参数都应该是即将运行的命令名称,但这不是强制性的。

结尾包含 “p” 的变体(execlp()execlpe()execvp()execvpe() )将使用 PATH 环境变量来查找程序 file。当环境被替换时(使用下一段讨论的 `exece变体之一),PATH变量将来自于新环境。其他变体execl()execle()execv()execve()不使用PATH` 变量来查找程序,因此 *path 必须包含正确的绝对或相对路径。

对于 execle()execlpe()execve()execvpe() (都以 “e” 结尾),env 参数是一个映射,用于定义新进程的环境变量(代替当前进程的环境变量)。而函数 execl()execlp()execv()execvp() 会将当前进程的环境变量过继给新进程。

某些平台上的 execve() 可以将 path 指定为打开的文件描述符。当前平台可能不支持此功能,可以使用 os.supports_fd 检查它是否支持。如果不可用,则使用它会抛出 NotImplementedError 异常。

引发一个 审计事件 os.exec,附带参数 pathargsenv

可用性: Unix, Windows。

3.3 新版功能: 新增支持将 execve()path 参数指定为打开的文件描述符。

在 3.6 版更改: 接受一个 path-like object。

os._exit(n)

以状态码 n 退出进程,不会调用清理处理程序,不会刷新 stdio,等等。

注解

退出的标准方法是使用 sys.exit(n)。而 _exit() 通常只应在 fork() 出的子进程中使用。

以下是已定义的退出代码,可以用于 _exit(),尽管它们不是必需的。这些退出代码通常用于 Python 编写的系统程序,例如邮件服务器的外部命令传递程序。

注解

其中部分退出代码在部分 Unix 平台上可能不可用,因为平台间存在差异。如果底层平台定义了这些常量,那上层也会定义。

os.EX_OK

退出代码,表示未发生任何错误。

可用性: Unix。

os.EX_USAGE

退出代码,表示命令使用不正确,如给出的参数数量有误。

可用性: Unix。

os.EX_DATAERR

退出代码,表示输入数据不正确。

可用性: Unix。

os.EX_NOINPUT

退出代码,表示某个输入文件不存在或不可读。

可用性: Unix。

os.EX_NOUSER

退出代码,表示指定的用户不存在。

可用性: Unix。

os.EX_NOHOST

退出代码,表示指定的主机不存在。

可用性: Unix。

os.EX_UNAVAILABLE

退出代码,表示所需的服务不可用。

可用性: Unix。

os.EX_SOFTWARE

退出代码,表示检测到内部软件错误。

可用性: Unix。

os.EX_OSERR

退出代码,表示检测到操作系统错误,例如无法 fork 或创建管道。

可用性: Unix。

os.EX_OSFILE

退出代码,表示某些系统文件不存在、无法打开或发生其他错误。

可用性: Unix。

os.EX_CANTCREAT

退出代码,表示无法创建用户指定的输出文件。

可用性: Unix。

os.EX_IOERR

退出代码,表示对某些文件进行读写时发生错误。

可用性: Unix。

os.EX_TEMPFAIL

退出代码,表示发生了暂时性故障。它可能并非意味着真正的错误,例如在可重试的情况下无法建立网络连接。

可用性: Unix。

os.EX_PROTOCOL

退出代码,表示协议交换是非法的、无效的或无法解读的。

可用性: Unix。

os.EX_NOPERM

退出代码,表示没有足够的权限执行该操作(但不适用于文件系统问题)。

可用性: Unix。

os.EX_CONFIG

退出代码,表示发生某种配置错误。

可用性: Unix。

os.EX_NOTFOUND

退出代码,表示的内容类似于“找不到条目”。

可用性: Unix。

os.fork()

Fork 出一个子进程。在子进程中返回 0,在父进程中返回子进程的进程号。如果发生错误,则抛出 OSError 异常。

注意,当从线程中使用 fork() 时,某些平台(包括 FreeBSD <= 6.3 和 Cygwin)存在已知问题。

引发一个 审计事件 os.fork,没有附带参数。

在 3.8 版更改: 不再支持在子解释器中调用 fork() (将抛出 RuntimeError 异常)。

警告

有关 SSL 模块与 fork() 结合的应用。

可用性: Unix。

os.forkpty()

Fork 出一个子进程,使用新的伪终端作为子进程的控制终端。返回一对 (pid, fd),其中 pid 在子进程中为 0,这是父进程中新子进程的进程号,而 fd 是伪终端主设备的文件描述符。对于更便于移植的方法,请使用 pty 模块。如果发生错误,则抛出 OSError 异常。

引发一个 审计事件 os.forkpty,没有附带参数。

在 3.8 版更改: 不再支持在子解释器中调用 forkpty() (将抛出 RuntimeError 异常)。

可用性: 某些 Unix。

os.kill(pid, sig)

将信号 sig 发送至进程 pid。特定平台上可用的信号常量定义在 signal 模块中。

Windows: signal.CTRL_C_EVENTsignal.CTRL_BREAK_EVENT 信号是特殊信号,只能发送给共享同一个控制台窗口的控制台进程,如某些子进程。sig 取任何其他值将导致该进程被 TerminateProcess API 无条件终止,且退出代码为 sig。Windows 版本的 kill() 还需要传入待结束进程的句柄。

另请参阅 signal.pthread_kill()

引发一个 审计事件 os.kill,附带参数 pidsig

3.2 新版功能: Windows 支持。

os.killpg(pgid, sig)

将信号 sig 发送给进程组 pgid

引发一个 审计事件 os.killpg,附带参数 pgidsig

可用性: Unix。

os.nice(increment)

将进程的优先级(nice 值)增加 increment,返回新的 nice 值。

可用性: Unix。

os.pidfd_open(pid, flags=0)

返回一个文件描述符,它指向进程 pid。该描述符可用于管理进程,避免出现竞争和信号。flags 参数提供给将来扩展使用,当前没有定义标志值。

可用性: Linux 5.3+。

3.9 新版功能.

os.plock(op)

将程序段锁定到内存中。op 的值(定义在 <sys/lock.h> 中)决定了哪些段被锁定。

可用性: Unix。

os.popen(cmd, mode=’r’, buffering=- 1)

打开一个管道,它通往 / 接受自命令 cmd*。返回值是连接到管道的文件对象,根据 *mode'r' (默认)还是 'w' 决定该对象可以读取还是写入。buffering 参数与内置函数 open() 相应的参数含义相同。返回的文件对象只能读写文本字符串,不能是字节类型。

如果子进程成功退出,则 close 方法返回 None。如果发生错误,则返回子进程的返回码。在 POSIX 系统上,如果返回码为正,则它就是进程返回值左移一个字节后的值。如果返回码为负,则进程是被信号终止的,返回码取反后就是该信号。(例如,如果子进程被终止,则返回值可能是 - signal.SIGKILL。)在 Windows 系统上,返回值包含子进程的返回码(有符号整数)。

在 Unix 上,waitstatus_to_exitcode() 可以将 close 方法的返回值(即退出状态,不能是 None)转换为退出码。在 Windows 上,close 方法的结果直接就是退出码(或 None )。

本方法是使用 subprocess.Popen 实现的,如需更强大的方法来管理和沟通子进程,请参阅该类的文档。

os.posix_spawn(path, argv, env, **, file_actions=None, setpgroup=None, resetids=False, setsid=False, setsigmask=(), setsigdef=(), scheduler=None*)

包装 posix_spawn() C 库 API,使其可以从 Python 调用。

大多数用户应使用 subprocess.run() 代替 posix_spawn()

仅位置参数 (Positional-only arguments) pathargsenvexecve() 中的类似。

path 形参是可执行文件的路径,path 中应当包含目录。 使用 posix_spawnp() 可传入不带目录的可执行文件。

file_actions 参数可以是由元组组成的序列,序列描述了对子进程中指定文件描述符采取的操作,这些操作会在 C 库实现的 fork()exec() 步骤间完成。每个元组的第一个元素必须是下面列出的三个类型指示符之一,用于描述元组剩余的元素:

  • os.POSIX_SPAWN_OPEN

    (os.POSIX_SPAWN_OPEN, fd, path, flags, mode)

    执行 os.dup2(os.open(path, flags, mode), fd)

  • os.POSIX_SPAWN_CLOSE

    (os.POSIX_SPAWN_CLOSE, fd)

    执行 os.close(fd)

  • os.POSIX_SPAWN_DUP2

    (os.POSIX_SPAWN_DUP2, fd, new_fd)

    执行 os.dup2(fd, new_fd)

这些元组对应于 C 库 posix_spawn_file_actions_addopen()posix_spawn_file_actions_addclose()posix_spawn_file_actions_adddup2() API 调用,它们为调用 posix_spawn() 自身做准备。

setpgroup 参数将子进程的进程组设置为指定值。如果指定值为 0,则子进程的进程组 ID 将与其进程 ID 相同。如果未设置 setpgroup 值,则子进程将继承父进程的进程组 ID。本参数对应于 C 库 POSIX_SPAWN_SETPGROUP 标志。

如果 resetids 参数为 True,则会将子进程的有效用户 ID 和有效组 ID 重置为父进程的实际用户 ID 和实际组 ID。如果该参数为 False,则子进程保留父进程的有效用户 ID 和有效组 ID。无论哪种情况,若在可执行文件上启用了 “设置用户 ID” 和 “设置组 ID” 权限位,它们将覆盖有效用户 ID 和有效组 ID 的设置。本参数对应于 C 库 POSIX_SPAWN_RESETIDS 标志。

如果 setsid 参数为 True,它将为 posix_spawn 新建一个会话 ID。setsid 需要 POSIX_SPAWN_SETSIDPOSIX_SPAWN_SETSID_NP 标志,否则会抛出 NotImplementedError 异常。

setsigmask 参数将信号掩码设置为指定的信号集合。如果未使用该参数,则子进程将继承父进程的信号掩码。本参数对应于 C 库 POSIX_SPAWN_SETSIGMASK 标志。

sigdef 参数将集合中所有信号的操作全部重置为默认。本参数对应于 C 库 POSIX_SPAWN_SETSIGDEF 标志。

scheduler 参数必须是一个元组,其中包含调度器策略(可选)以及携带了调度器参数的 sched_param 实例。在调度器策略所在位置为 None 表示未提供该值。本参数是 C 库 POSIX_SPAWN_SETSCHEDPARAMPOSIX_SPAWN_SETSCHEDULER 标志的组合。

引发一个 审计事件 os.posix_spawn,附带参数 pathargvenv

3.8 新版功能.

可用性: Unix。

os.posix_spawnp(path, argv, env, **, file_actions=None, setpgroup=None, resetids=False, setsid=False, setsigmask=(), setsigdef=(), scheduler=None*)

包装 posix_spawnp() C 库 API,使其可以从 Python 调用。

posix_spawn() 相似,但是系统会在 PATH 环境变量指定的目录列表中搜索可执行文件 executable (与 execvp(3) 相同)。

引发一个 审计事件 os.posix_spawn,附带参数 pathargvenv

3.8 新版功能.

可用性。

os.register_at_fork(**, before=None, after_in_parent=None, after_in_child=None*)

注册可调用对象,在使用 os.fork() 或类似的进程克隆 API 派生新的子进程时,这些对象会运行。参数是可选的,且为仅关键字 (Keyword-only) 参数。每个参数指定一个不同的调用点。

  • before 是一个函数,在 fork 子进程前调用。
  • after_in_parent 是一个函数,在 fork 子进程后从父进程调用。
  • after_in_child 是一个函数,从子进程中调用。

只有希望控制权回到 Python 解释器时,才进行这些调用。典型的 子进程 启动时不会触发它们,因为子进程不会重新进入解释器。

在注册的函数中,用于 fork 前运行的函数将按与注册相反的顺序调用。用于 fork 后(从父进程或子进程)运行的函数按注册顺序调用。

注意,第三方 C 代码的 fork() 调用可能不会调用这些函数,除非它显式调用了 PyOS_BeforeFork()PyOS_AfterFork_Parent()PyOS_AfterFork_Child()

函数注册后无法注销。

可用性: Unix。

3.7 新版功能.

os.spawnl(mode, path, )

os.spawnle(mode, path, , env)

os.spawnlp(mode, file, )

os.spawnlpe(mode, file, , env)

os.spawnv(mode, path, args)

os.spawnve(mode, path, args, env)

os.spawnvp(mode, file, args)

os.spawnvpe(mode, file, args, env)

在新进程中执行程序 path

(注意,subprocess 模块提供了更强大的工具来生成新进程并跟踪执行结果,使用该模块比使用这些函数更好。尤其应当检查 使用 subprocess 模块替换旧函数 部分。)

modeP_NOWAIT 时,本函数返回新进程的进程号。modeP_WAIT 时,如果进程正常退出,返回退出代码,如果被终止,返回 -signal,其中 signal 是终止进程的信号。在 Windows 上,进程号实际上是进程句柄,因此可以与 waitpid() 函数一起使用。

注意在 VxWorks 上,新进程被终止时,本函数不会返回 -signal,而是会抛出 OSError 异常。

spawn* 函数的 “l” 和 “v” 变体不同在于命令行参数的传递方式。如果在编码时固定了参数数量,则 “l” 变体可能是最方便的,各参数作为 spawnl*() 函数的附加参数传入即可。当参数数量可变时,”v” 变体更方便,参数以列表或元组的形式作为 args 参数传递。在这两种情况下,子进程的第一个参数都必须是即将运行的命令名称。

结尾包含第二个 “p” 的变体(spawnlp()spawnlpe()spawnvp()spawnvpe())将使用 PATH 环境变量来查找程序 file。当环境被替换时(使用下一段讨论的 `spawne变体之一),PATH变量将来自于新环境。其他变体spawnl()spawnle()spawnv()spawnve()不使用PATH` 变量来查找程序,因此 *path 必须包含正确的绝对或相对路径。

对于 spawnle()spawnlpe()spawnve()spawnvpe() (都以 “e” 结尾),env 参数是一个映射,用于定义新进程的环境变量(代替当前进程的环境变量)。而函数 spawnl()spawnlp()spawnv()spawnvp() 会将当前进程的环境变量过继给新进程。注意,env 字典中的键和值必须是字符串。无效的键或值将导致函数出错,返回值为 127

例如,以下对 spawnlp()spawnvpe() 的调用是等效的:

import os
os.spawnlp(os.P_WAIT, 'cp', 'cp', 'index.html', '/dev/null')
L = ['cp', 'index.html', '/dev/null']
os.spawnvpe(os.P_WAIT, 'cp', L, os.environ)

引发一个 审计事件 os.spawn,附带参数 modepathargsenv

可用性: Unix, Windows。spawnlp()spawnlpe()spawnvp()spawnvpe() 在 Windows 上不可用。spawnle()spawnve() 在 Windows 上不是线程安全的,建议使用 subprocess 模块替代。

在 3.6 版更改: 接受一个 path-like object。

os.P_NOWAIT
os.P_NOWAITO

spawn* 系列函数的 mode 参数的可取值。如果给出这些值中的任何一个,则 spawn*() 函数将在创建新进程后立即返回,且返回值为进程号。

可用性: Unix, Windows。

os.P_WAIT

spawn* 系列函数的 mode 参数的可取值。如果将 mode 指定为该值,则 spawn*() 函数将在新进程运行完毕后返回,运行成功则返回进程的退出代码,被信号终止则返回 -signal

可用性: Unix, Windows。

os.P_DETACH
os.P_OVERLAY

spawn* 系列函数的 mode 参数的可取值。它们比上面列出的值可移植性差。P_DETACHP_NOWAIT 相似,但是新进程会与父进程的控制台脱离。使用 P_OVERLAY 则会替换当前进程,spawn* 函数将不会返回。

可用性: Windows。

os.startfile(path, operation, cwd)

使用已关联的应用程序打开文件。

operation 未指定或指定为 'open' 时,这类似于在 Windows 资源管理器中双击文件,或在交互式命令行中将文件名作为 start 命令的参数:通过扩展名相关联的应用程序(如果有)打开文件。

当指定另一个 operation 时,它必须是一个“命令动词” (“command verb”),该词指定对文件执行的操作。Microsoft 文档中的常用动词有 'print''edit' (用于文件),以及 'explore''find' (用于目录)。

在启动某个应用程序时,arguments 将作为一个字符串传入。若是打开某个文档,此参数可能没什么效果。

默认工作目录是继承而来的,但可以通过 cwd 参数进行覆盖。且应为绝对路径。相对路径 path 将据此参数进行解析。

show_cmd 可用于覆盖默认的窗口样式。是否生效则取决于被启动的应用程序。应为 Win32 函数 ShellExecute() 所支持的整数值。

在关联的应用程序启动后,startfile() 就会立即返回。 没有提供等待应用程序关闭的选项,也没有办法获得应用程序的退出状态。 path 形参是基于当前目录或 cwd 的相对路径。 如果要使用绝对路径,请确保第一个字符不为斜杠 ('/') 。 请用 pathlibos.path.normpath() 函数来保证路径已按照 Win32 的要求进行了正确的编码。

为了减少解释器的启动开销,直到第一次调用本函数后,才解析 Win32 ShellExecute() 函数。如果无法解析该函数,则抛出 NotImplementedError 异常。

引发一个 审计事件 os.startfile,附带参数 pathoperation

引发一条 审计事件 os.startfile/2,附带参数为 pathoperationargumentscwdshow_cmd

可用性: Windows。

在 3.10 版更改: 加入了 argumentscwdshow_cmd 参数,以及 os.startfile/2 审计事件。

os.system(command)

在子外壳程序中执行此命令(一个字符串)。 这是通过调用标准 C 函数 system() 来实现的,并受到同样的限制。 对 sys.stdin 的更改等不会反映在所执行命令的环境中。 如果 command 生成了任何输出,它将被发送到解释器的标准输出流。 C 标准没有指明这个 C 函数返回值的含义,因此这个 Python 函数的返回值取决于具体系统。

在 Unix 上,返回值为进程的退出状态,以针对 wait() 而指定的格式进行编码。

在 Windows 上,返回值是运行 command 后系统 Shell 返回的值。该 Shell 由 Windows 环境变量 COMSPEC: 给出:通常是 cmd.exe,它会返回命令的退出状态。在使用非原生 Shell 的系统上,请查阅 Shell 的文档。

subprocess 模块提供了更强大的工具来生成新进程并跟踪执行结果,使用该模块比使用本函数更好。

在 Unix 上,waitstatus_to_exitcode() 可以将返回值(即退出状态)转换为退出码。在 Windows 上,返回值就是退出码。

引发一个 审计事件 os.system,附带参数 command

可用性: Unix, Windows。

os.times()

返回当前的全局进程时间。返回值是一个有 5 个属性的对象:

  • user - 用户时间
  • system - 系统时间
  • children_user - 所有子进程的用户时间
  • children_system - 所有子进程的系统时间
  • elapsed - 从过去的固定时间点起,经过的真实时间

为了向后兼容,该对象的行为也类似于五元组,按照 usersystemchildren_userchildren_systemelapsed 顺序组成。

在 Windows 上,只有 usersystem 是已知的,其他属性均为零。

可用性: Unix, Windows。

在 3.3 版更改: 返回结果的类型由元组变成一个类似元组的对象,同时具有命名的属性。

os.wait()

等待子进程执行完毕,返回一个元组,包含其 pid 和退出状态指示:一个 16 位数字,其低字节是终止该进程的信号编号,高字节是退出状态码(信号编号为零的情况下),如果生成了核心文件,则低字节的高位会置位。

可以使用 waitstatus_to_exitcode() 来将退出状态转换为退出码。

可用性: Unix。

参见

waitpid() 可以等待特定的子进程执行完毕,且支持更多选项。

os.waitid(idtype, id, options)

等待一个或多个子进程执行完毕。idtype 可以是 P_PID, P_PGID, P_ALL, 或 P_PIDFD (Linux 可用)。id 指定要等待的 pid。options 是由 WEXITEDWSTOPPEDWCONTINUED 中的一个或多个进行或运算构造的,且额外可以与 WNOHANGWNOWAIT 进行或运算。返回值是一个对象,对应着 siginfo_t 结构体中的数据,即: si_pid, si_uid, si_signo, si_status, si_codeNone (如果指定了 WNOHANG 且没有子进程处于等待状态)。

可用性: Unix。

3.3 新版功能.

os.P_PID
os.P_PGID
os.P_ALL

waitid()idtype 参数的可取值。它们影响 id 的解释方式。

可用性: Unix。

3.3 新版功能.

os.P_PIDFD

这是仅 Linux 上存在的一种 idtype*,它表示 *id 是指向一个进程的文件描述符。

可用性: Linux 5.4+

3.9 新版功能.

os.WEXITED
os.WSTOPPED
os.WNOWAIT

用于 waitid()options 参数的标志位,指定要等待的子进程信号。

可用性: Unix。

3.3 新版功能.

os.CLD_EXITED
os.CLD_KILLED
os.CLD_DUMPED
os.CLD_TRAPPED
os.CLD_STOPPED
os.CLD_CONTINUED

waitid() 返回的结果中,si_code 的可取值。

可用性: Unix。

3.3 新版功能.

在 3.9 版更改: 添加了 CLD_KILLEDCLD_STOPPED 值。

os.waitpid(pid, options)

本函数的细节在 Unix 和 Windows 上有不同之处。

在 Unix 上:等待进程号为 pid 的子进程执行完毕,返回一个元组,内含其进程 ID 和退出状态指示(编码与 wait() 相同)。调用的语义受整数 options 的影响,常规操作下该值应为 0

如果 pid 大于 0,则 waitpid() 会获取该指定进程的状态信息。如果 pid0,则获取当前进程所在进程组中的所有子进程的状态。如果 pid-1,则获取当前进程的子进程状态。如果 pid 小于 -1,则获取进程组 -pidpid 的绝对值)中所有进程的状态。

当系统调用返回 -1 时,将抛出带有错误码的 OSError 异常。

在 Windows 上:等待句柄为 pid 的进程执行完毕,返回一个元组,内含 pid 以及左移 8 位后的退出状态码(移位简化了跨平台使用本函数)。小于或等于 0pid 在 Windows 上没有特殊含义,且会抛出异常。整数值 options 无效。pid 可以指向任何 ID 已知的进程,不一定是子进程。调用 spawn* 函数时传入 P_NOWAIT 将返回合适的进程句柄。

可以使用 waitstatus_to_exitcode() 来将退出状态转换为退出码。

在 3.5 版更改: 如果系统调用被中断,但信号处理程序没有触发异常,此函数现在会重试系统调用,而不是触发 InterruptedError 异常 (原因详见 PEP 475)。

os.wait3(options)

waitpid() 相似,差别在于没有进程 ID 参数,且返回一个 3 元组,其中包括子进程 ID,退出状态指示和资源使用信息。option 参数与传入 waitpid()wait4() 的相同。

可以使用 waitstatus_to_exitcode() 来将退出状态转换为退出码。

可用性: Unix。

os.wait4(pid, options)

waitpid() 相似,差别在本方法返回一个 3 元组,其中包括子进程 ID,退出状态指示和资源使用信息。wait4() 的参数与 waitpid() 的参数相同。

可以使用 waitstatus_to_exitcode() 来将退出状态转换为退出码。

可用性: Unix。

os.waitstatus_to_exitcode(status)

将等待状态转换为退出码。

在 Unix 上:

  • 如果进程正常退出(当 WIFEXITED(status) 为真值),则返回进程退出状态 (返回 WEXITSTATUS(status)): 结果值大于等于 0。
  • 如果进程被信号终止(当 WIFSIGNALED(status) 为真值),则返回 -signum 其中 signum 为导致进程终止的信号数值 (返回 -WTERMSIG(status)): 结果值小于 0。
  • 否则将抛出 ValueError 异常。

在 Windows 上,返回 status 右移 8 位的结果。

在 Unix 上,如果进程正被追踪或 waitpid() 附带 WUNTRACED 选项被调用,则调用者必须先检查 WIFSTOPPED(status) 是否为真值。 如果 WIFSTOPPED(status) 为真值则此函数不可被调用。

参见

WIFEXITED(), WEXITSTATUS(), WIFSIGNALED(), WTERMSIG(), WIFSTOPPED(), WSTOPSIG() 函数。

3.9 新版功能.

os.WNOHANG

用于 waitpid() 的选项,如果没有立即可用的子进程状态,则立即返回。在这种情况下,函数返回 (0, 0)

可用性: Unix。

os.WCONTINUED

被任务控制 (job control) 停止的子进程,如果上次报告状态后已恢复运行,则此选项将报告这些子进程。

可用性: 部分 Unix 系统。

os.WUNTRACED

已停止的子进程,如果自停止以来尚未报告其当前状态,则此选项将报告这些子进程。

可用性: Unix。

下列函数采用进程状态码作为参数,状态码由 system()wait()waitpid() 返回。它们可用于确定进程上发生的操作。

os.WCOREDUMP(status)

如果为该进程生成了核心转储,返回 True,否则返回 False

此函数应当仅在 WIFSIGNALED() 为真值时使用。

可用性: Unix。

os.WIFCONTINUED(status)

如果一个已停止的子进程通过传送 SIGCONT 获得恢复(如果该进程是从任务控制停止后再继续的)则返回 True,否则返回 False

参见 WCONTINUED 选项。

可用性: Unix。

os.WIFSTOPPED(status)

如果进程是通过传送一个信号来停止的则返回 True,否则返回 False

WIFSTOPPED() 只有在当 waitpid() 调用是通过使用 WUNTRACED 选项来完成或者当该进程正被追踪时 才返回 True

可用性: Unix。

os.WIFSIGNALED(status)

如果进程是通过一个信号来终止的则返回 True ,否则返回 False

可用性: Unix。

os.WIFEXITED(status)

如果进程正常终止退出则返回 True,也就是说通过调用 exit()_exit(),或者通过从 main() 返回;在其他情况下则返回 False

可用性: Unix。

os.WEXITSTATUS(status)

返回进程退出状态。

此函数应当仅在 WIFEXITED() 为真值时使用。

可用性: Unix。

os.WSTOPSIG(status)

返回导致进程停止的信号。

此函数应当仅在 WIFSTOPPED() 为真值时使用。

可用性: Unix。

os.WTERMSIG(status)

返回导致进程终止的信号的编号。

此函数应当仅在 WIFSIGNALED() 为真值时使用。

可用性: Unix。

调度器接口

这些函数控制操作系统如何为进程分配 CPU 时间。 它们仅在某些 Unix 平台上可用。 更多细节信息请查阅你所用 Unix 的指南页面。

3.3 新版功能.

以下调度策略如果被操作系统支持就会对外公开。

os.SCHED_OTHER

默认调度策略。

os.SCHED_BATCH

用于 CPU 密集型进程的调度策略,它会尽量为计算机中的其余任务保留交互性。

os.SCHED_IDLE

用于极低优先级的后台任务的调度策略。

os.SCHED_SPORADIC

用于偶发型服务程序的调度策略。

os.SCHED_FIFO

先进先出的调度策略。

os.SCHED_RR

循环式的调度策略。

os.SCHED_RESET_ON_FORK

此旗标可与任何其他调度策略进行 OR 运算。 当带有此旗标的进程设置分叉时,其子进程的调度策略和优先级会被重置为默认值。

class os.sched_param(sched_priority)

这个类表示在 sched_setparam(), sched_setscheduler()sched_getparam() 中使用的可修改调度形参。 它属于不可变对象。

目前它只有一个可能的形参:

  • sched_priority

    一个调度策略的调度优先级。

os.sched_get_priority_min(policy)

获取 policy 的最低优先级数值。 policy 是以上调度策略常量之一。

os.sched_get_priority_max(policy)

获取 policy 的最高优先级数值。 policy 是以上调度策略常量之一。

os.sched_setscheduler(pid, policy, param)

设置 PID 为 pid 的进程的调度策略。pid 为 0 指的是调用本方法的进程。policy 是以上调度策略常量之一。param 是一个 sched_param 实例。

os.sched_getscheduler(pid)

返回 PID 为 pid 的进程的调度策略。pid 为 0 指的是调用本方法的进程。返回的结果是以上调度策略常量之一。

os.sched_setparam(pid, param)

设置 PID 为 pid 的进程的调度参数。 pid 为 0 表示调用方过程。 param 是一个 sched_param 实例。

os.sched_getparam(pid)

返回 PID 为 pid 的进程的调度参数为一个 sched_param 实例。pid 为 0 指的是调用本方法的进程。

os.sched_rr_get_interval(pid)

返回 PID 为 pid 的进程在时间片轮转调度下的时间片长度(单位为秒)。pid 为 0 指的是调用本方法的进程。

os.sched_yield()

自愿放弃 CPU。

os.sched_setaffinity(pid, mask)

将 PID 为 pid 的进程(为零则为当前进程)限制到一组 CPU 上。mask 是整数的可迭代对象,表示应将进程限制在其中的一组 CPU。

os.sched_getaffinity(pid)

返回 PID 为 pid 的进程(为零则为当前进程)被限制到的那一组 CPU。

其他系统信息

os.confstr(name)

返回字符串格式的系统配置信息。name 指定要查找的配置名称,它可以是字符串,是一个系统已定义的名称,这些名称定义在不同标准(POSIX,Unix 95,Unix 98 等)中。一些平台还定义了额外的其他名称。当前操作系统已定义的名称在 confstr_names 字典的键中给出。对于未包含在该映射中的配置名称,也可以传递一个整数作为 name

如果 name 指定的配置值未定义,返回 None

如果 name 是一个字符串且不是已定义的名称,将抛出 ValueError 异常。如果当前系统不支持 name 指定的配置名称,即使该名称存在于 confstr_names,也会抛出 OSError 异常,错误码为 errno.EINVAL

可用性: Unix。

os.confstr_names

字典,表示映射关系,为 confstr() 可接受名称与操作系统为这些名称定义的整数值之间的映射。这可用于判断系统已定义了哪些名称。

可用性: Unix。

os.cpu_count()

返回系统的 CPU 数量。不确定则返回 None

该数量不同于当前进程可以使用的CPU数量。可用的CPU数量可以由 len(os.sched_getaffinity(0)) 方法获得。

3.4 新版功能.

os.getloadavg()

返回系统运行队列中最近 1、5 和 15 分钟内的平均进程数。无法获得平均负载则抛出 OSError 异常。

可用性: Unix。

os.sysconf(name)

返回整数格式的系统配置信息。如果 name 指定的配置值未定义,返回 -1。对 confstr()name 参数的注释在此处也适用。当前已知的配置名称在 sysconf_names 字典中提供。

可用性: Unix。

os.sysconf_names

字典,表示映射关系,为 sysconf() 可接受名称与操作系统为这些名称定义的整数值之间的映射。这可用于判断系统已定义了哪些名称。

可用性: Unix。

以下数据值用于支持对路径本身的操作。所有平台都有定义。

对路径的高级操作在 os.path 模块中定义。

os.curdir

操作系统用来表示当前目录的常量字符串。在 Windows 和 POSIX 上是 '.'。在 os.path 中也可用。

os.pardir

操作系统用来表示父目录的常量字符串。在 Windows 和 POSIX 上是 '..'。在 os.path 中也可用。

os.sep

操作系统用来分隔路径不同部分的字符。在 POSIX 上是 '/',在 Windows 上是是 '\\'。注意,仅了解它不足以能解析或连接路径,请使用 os.path.split()os.path.join(),但它有时是有用的。在 os.path 中也可用。

os.altsep

操作系统用来分隔路径不同部分的替代字符。如果仅存在一个分隔符,则为 None。在 sep 是反斜杠的 Windows 系统上,该值被设为 '/'。在 os.path 中也可用。

os.extsep

分隔基本文件名与扩展名的字符,如 os.py 中的 '.'。在 os.path 中也可用。

os.pathsep

操作系统通常用于分隔搜索路径(如 PATH)中不同部分的字符,如 POSIX 上是 ':',Windows 上是 ';'。在 os.path 中也可用。

os.defpath

在环境变量没有 'PATH' 键的情况下,exec*p* and spawn*p* 使用的默认搜索路径。在 os.path 中也可用。

os.linesep

当前平台用于分隔(或终止)行的字符串。它可以是单个字符,如 POSIX 上是 '\n',也可以是多个字符,如 Windows 上是 '\r\n'。在写入以文本模式(默认模式)打开的文件时,请不要使用 os.linesep 作为行终止符,请在所有平台上都使用一个 '\n' 代替。

os.devnull

空设备的文件路径。如 POSIX 上为 '/dev/null',Windows 上为 'nul'。在 os.path 中也可用。

os.RTLD_LAZY
os.RTLD_NOW
os.RTLD_GLOBAL
os.RTLD_LOCAL
os.RTLD_NODELETE
os.RTLD_NOLOAD
os.RTLD_DEEPBIND

setdlopenflags()getdlopenflags() 函数所使用的标志。

3.3 新版功能.

随机数

os.getrandom(size, flags=0)

获得最多为 size 的随机字节。本函数返回的字节数可能少于请求的字节数。

这些字节可用于为用户空间的随机数生成器提供种子,或用于加密目的。

getrandom() 依赖于从设备驱动程序和其他环境噪声源收集的熵。不必要地读取大量数据将对使用 /dev/random/dev/urandom 设备的其他用户产生负面影响。

flags 参数是一个位掩码,可以是零个或多个下列值以或运算组合: os.GRND_RANDOMGRND_NONBLOCK

可用性:Linux 3.17 或更高版本。

3.6 新版功能.

os.urandom(size)

返回大小为 size 的字符串,它是适合加密使用的随机字节。

本函数从系统指定的随机源获取随机字节。对于加密应用程序,返回的数据应有足够的不可预测性,尽管其确切的品质取决于操作系统的实现。

在 Linux 上,如果 getrandom() 系统调用可用,它将以阻塞模式使用:阻塞直到系统的 urandom 熵池初始化完毕(内核收集了 128 位熵)。原理请参阅 PEP 524。在 Linux 上,getrandom() 可以以非阻塞模式(使用 GRND_NONBLOCK 标志)获取随机字节,或者轮询直到系统的 urandom 熵池初始化完毕。

在类 Unix 系统上,随机字节是从 /dev/urandom 设备读取的。如果 /dev/urandom 设备不可用或不可读,则抛出 NotImplementedError 异常。

在 Windows 上将使用 CryptGenRandom()

参见

secrets 模块提供了更高级的功能。所在平台会提供随机数生成器,有关其易于使用的接口。

在 3.6.0 版更改: 在 Linux 上,getrandom() 现在以阻塞模式使用,以提高安全性。

在 3.5.2 版更改: 在 Linux 上,如果 getrandom() 系统调用阻塞(urandom 熵池尚未初始化完毕),则退回一步读取 /dev/urandom

在 3.5 版更改: 在 Linux 3.17 和更高版本上,现在使用 getrandom() 系统调用(如果可用)。在 OpenBSD 5.6 和更高版本上,现在使用 getentropy() C 函数。这些函数避免了使用内部文件描述符。

os.GRND_NONBLOCK

默认情况下,从 /dev/random 读取时,如果没有可用的随机字节,则 getrandom() 会阻塞;从 /dev/urandom 读取时,如果熵池尚未初始化,则会阻塞。

如果设置了 GRND_NONBLOCK 标志,则这些情况下 getrandom() 不会阻塞,而是立即抛出 BlockingIOError 异常。

3.6 新版功能.

os.GRND_RANDOM

如果设置了此标志位,那么将从 /dev/random 池而不是 /dev/urandom 池中提取随机字节。

3.6 新版功能.

io —- 处理流的核心工具

源代码: Lib/io.py


概述

io 模块提供了 Python 用于处理各种 I/O 类型的主要工具。三种主要的 I/O类型分别为: 文本 I/O, 二进制 I/O原始 I/O*。这些是泛型类型,有很多种后端存储可以用在他们上面。一个隶属于任何这些类型的具体对象被称作 file object。 其他同类的术语还有 *流类文件对象

独立于其类别,每个具体流对象也将具有各种功能:它可以是只读,只写或读写。它还可以允许任意随机访问(向前或向后寻找任何位置),或仅允许顺序访问(例如在套接字或管道的情况下)。

所有流对提供给它们的数据类型都很敏感。例如将 str 对象给二进制流的 write() 方法会引发 TypeError。将 bytes 对象提供给文本流的 write() 方法也是如此。

在 3.3 版更改: 由于 IOError 现在是 OSError 的别名,因此用于引发 IOError 的操作现在会引发 OSError

文本 I/O

文本I/O预期并生成 str 对象。这意味着,无论何时后台存储是由字节组成的(例如在文件的情况下),数据的编码和解码都是透明的,并且可以选择转换特定于平台的换行符。

创建文本流的最简单方法是使用 open(),可以选择指定编码:

f = open("myfile.txt", "r", encoding="utf-8")

内存中文本流也可以作为 StringIO 对象使用:

f = io.StringIO("some initial text data")

TextIOBase 的文档中详细描述了文本流的API

二进制 I/O

二进制I/O(也称为缓冲I/O)预期 bytes-like objects 并生成 bytes 对象。不执行编码、解码或换行转换。这种类型的流可以用于所有类型的非文本数据,并且还可以在需要手动控制文本数据的处理时使用。

创建二进制流的最简单方法是使用 open(),并在模式字符串中指定 'b'

f = open("myfile.jpg", "rb")

内存中二进制流也可以作为 BytesIO 对象使用:

f = io.BytesIO(b"some initial binary data: \x00\x01")

BufferedIOBase 的文档中详细描述了二进制流 API。

其他库模块可以提供额外的方式来创建文本或二进制流。

原始 I/O

原始 I/O(也称为 非缓冲 I/O)通常用作二进制和文本流的低级构建块。用户代码直接操作原始流的用法非常罕见。不过,可以通过在禁用缓冲的情况下以二进制模式打开文件来创建原始流:

f = open("myfile.jpg", "rb", buffering=0)

RawIOBase 的文档中详细描述了原始流的API

文本编码格式

TextIOWrapperopen() 的默认编码格式取决于语言区域设置 (locale.getpreferredencoding(False)).

但是,很多开发者在打开以 UTF-8 编码的文本文件 (例如 JSON, TOML, Markdown 等等…) 时会忘记指定编码格式,因为大多数 Unix 平台默认使用 UTF-8 语言区域。 这会导致各种错误因为大多数 Windows 用户的语言区域编码格式并不是 UTF-8。 例如:

# May not work on Windows when non-ASCII characters in the file.
with open("README.md") as f:
    long_description = f.read()

此外,虽然暂时还没有确定的计划,但 Python 可能会在未来将默认的文本文件编码格式改为 UTF-8。

为此,强烈建议你在打开文本文件时显式地指定编码格式。 如果你想要使用 UTF-8,请传入 encoding="utf-8"。 要使用当前语言区域的编码格式,encoding="locale" 已在 Python 3.10 中被支持。

当你需要在 Windows 上运行尝试使用默认语言区域的编码格式打开使用 UTF-8 的文件的现有代码时,你可以启动 UTF-8 模式。 参见 Windows 上的 UTF-8 模式。

选择性的 EncodingWarning

3.10 新版功能: 请参阅 PEP 597 了解详情。

要找出哪里使用了默认语言区域的编码格式,你可以启用 -X warn_default_encoding 命令行选项或设置 PYTHONWARNDEFAULTENCODING 环境变量,这将在使用默认编码格式时发出 EncodingWarning

如果你提供了使用 open()TextIOWrapper 的 API 并将 encoding=None 作为形参传入,你可以使用 text_encoding() 以便 API 的调用方在没有传入 encoding 的时候将发出 EncodingWarning。 但是,对于新的 API 请考虑默认就使用 UTF-8 (即 encoding="utf-8")。

高阶模块接口

io.DEFAULT_BUFFER_SIZE

包含模块缓冲 I/O 类使用的默认缓冲区大小的 int。 在可能的情况下 open() 将使用文件的 blksize(由 os.stat() 获得)。

io.open(file, mode=’r’, buffering=- 1, encoding=None, errors=None, newline=None, closefd=True, opener=None)

这是内置的 open() 函数的别名。

open 附带参数 pathmodeflags 会引发 审计事件。

io.open_code(path)

'rb' 模式打开提供的文件。如果目的是将文件内容做为可执行代码,则应使用此函数。

path 应当为 str 类型并且是一个绝对路径。

此函数的行为可以由对 PyFile_SetOpenCodeHook() 的先期调用所重载。 但是,如果 pathstr 类型并且是一个绝对路径,open_code(path) 的行为应当总是与 open(path, 'rb') 一致。 重载此行为的目的是为了给文件附加额外的验证或预处理。

3.8 新版功能.

io.text_encoding(encoding, stacklevel=2)

这是一个针对使用 open()TextIOWrapper 的可调用对象的辅助函数并且具有 encoding=None 形参。

此函数会返回 encoding*,如果它不为 None 的话,或是 "locale" ,如果 *encodingNone 的话。

如果 sys.flags.warn_default_encoding 为真值并且 encoding 为 None 则此函数会发出 EncodingWarningstacklevel 指定在哪里发出警告。 例如:

def read_text(path, encoding=None):
    encoding = io.text_encoding(encoding)  # stacklevel=2
    with open(path, encoding) as f:
        return f.read()

在这个例子中,将为 read_text() 的调用方发出 EncodingWarning

3.10 新版功能.

exception io.BlockingIOError

这是内置的 BlockingIOError 异常的兼容性别名。

exception io.UnsupportedOperation

在流上调用不支持的操作时引发的继承 OSErrorValueError 的异常。

包含标准IO流: sys.stdin, sys.stdoutsys.stderr

类的层次结构

I/O 流被安排为按类的层次结构实现。 首先是 抽象基类 (ABC),用于指定流的各种类别,然后是提供标准流实现的具体类。

注解

抽象基类还提供某些方法的默认实现,以帮助实现具体的流类。例如 BufferedIOBase 提供了 readinto()readline() 的未优化实现。

I/O层次结构的顶部是抽象基类 IOBase 。它定义了流的基本接口。但是请注意,对流的读取和写入之间没有分离。如果实现不支持指定的操作,则会引发 UnsupportedOperation

抽象基类 RawIOBaseIOBase 的子类。它负责将字节读取和写入流中。 RawIOBase 的子类 FileIO 提供计算机文件系统中文件的接口。

抽象基类 BufferedIOBase 继承了 IOBase ,处理原始二进制流( RawIOBase )上的缓冲。其子类 BufferedWriterBufferedReaderBufferedRWPair 分别缓冲可读、可写以及可读写的原始二进制流。 BufferedRandom 提供了带缓冲的可随机访问流接口。 BufferedIOBase 的另一个子类 BytesIO 是内存中字节流。

抽象基类 TextIOBase 继承了 IOBase 。它处理可表示文本的流,并处理字符串的编码和解码。类 TextIOWrapper 继承了 TextIOBase ,是原始缓冲流( BufferedIOBase )的缓冲文本接口。最后, StringIO 是文本的内存流。

参数名不是规范的一部分,只有 open() 的参数才用作关键字参数。

下表总结了抽象基类提供的 io 模块:

抽象基类 继承 抽象方法 Mixin方法和属性
IOBase fileno, seek, 和 truncate close, closed, **enter**, **exit**, flush, isatty, **iter**, **next**, readable, readline, readlines, seekable, tell, writablewritelines
RawIOBase IOBase readintowrite 继承 IOBase 方法, read, 和 readall
BufferedIOBase IOBase detach, read, read1, 和 write 继承 IOBase 方法, readinto, 和 readinto1
TextIOBase IOBase detach, read, readline, 和 write 继承 IOBase 方法, encoding, errors, 和 newlines

I/O 基类

class io.IOBase

所有 I/O 类的抽象基类,作用于字节流。没有公共构造函数。

此类为许多方法提供了空的抽象实现,派生类可以有选择地重写。默认实现代表一个无法读取、写入或查找的文件。

尽管 IOBase 没有声明 read()write() ,因为它们的签名会有所不同,但是实现和客户端应该将这些方法视为接口的一部分。此外,当调用不支持的操作时可能会引发 ValueError (或 UnsupportedOperation )。

从文件读取或写入文件的二进制数据的基本类型为 bytes 。其他 bytes-like objects 也可以作为方法参数。文本I/O类使用 str 数据。

请注意,在关闭的流上调用任何方法(甚至查询)都是未定义的(undefined)。在这种情况下,实现可能会引发 ValueError

IOBase (及其子类)支持迭代器协议,这意味着可以迭代 IOBase 对象以产生流中的行。根据流是二进制流(产生字节)还是文本流(产生字符串),行的定义略有不同。请参见下面的 readline()

IOBase 也是一个上下文管理器,因此支持 with 语句。 在这个示例中,file 将在 with 语句块执行完成之后被关闭 —- 即使是发生了异常:

with open('spam.txt', 'w') as file:
    file.write('Spam and eggs!')

IOBase 提供以下数据属性和方法:

  • close()

    刷新并关闭此流。如果文件已经关闭,则此方法无效。文件关闭后,对文件的任何操作(例如读取或写入)都会引发 ValueError

    为方便起见,允许多次调用此方法。但是,只有第一个调用才会生效。

  • closed

    如果流已关闭,则返回 True。

  • fileno()

    返回流的底层文件描述符(整数)—-如果存在。如果 IO 对象不使用文件描述符,则会引发 OSError

  • flush()

    刷新流的写入缓冲区(如果适用)。这对只读和非阻塞流不起作用。

  • isatty()

    如果流是交互式的(即连接到终端/tty设备),则返回 True

  • readable()

    如果可以读取流,则返回 True 。否则为 False ,且 read() 将引发 OSError 错误。

  • readline(size=- 1)

    从流中读取并返回一行。如果指定了 size*,将至多读取 *size 个字节。

    对于二进制文件行结束符总是 b'\n';对于文本文件,可以用将 newline 参数传给 open() 的方式来选择要识别的行结束符。

  • readlines(hint=- 1)

    从流中读取并返回包含多行的列表。可以指定 hint 来控制要读取的行数:如果(以字节/字符数表示的)所有行的总大小超出了 hint 则将不会读取更多的行。

    0 或更小的 hint 值以及 None,会被视为没有 hint。

    请注意使用 for line in file: ... 就足够对文件对象进行迭代了,可以不必调用 file.readlines()

  • seek(offset, whence=SEEK_SET)

    将流位置修改到给定的字节 offset*。 *offset 将相对于由 whence 指定的位置进行解析。 whence 的默认值为 SEEK_SETwhence 的可用值有:

    • SEEK_SET0 — 流的开头(默认值);offset 应为零或正值
    • SEEK_CUR or 1 — 当前流位置;offset 可以为负值
    • SEEK_END or 2 — 流的末尾;offset 通常为负值

    返回新的绝对位置。

    3.1 新版功能: SEEK_* 常量.

    3.3 新版功能: 某些操作系统还可支持其他的值,例如 os.SEEK_HOLEos.SEEK_DATA。特定文件的可用值还会取决于它是以文本还是二进制模式打开。

  • seekable()

    如果流支持随机访问则返回 True。 如为 False,则 seek(), tell()truncate() 将引发 OSError

  • tell()

    返回当前流的位置。

  • truncate(size=None)

    将流的大小调整为给定的 size 个字节(如果未指定 size 则调整至当前位置)。 当前的流位置不变。 这个调整操作可扩展或减小当前文件大小。 在扩展的情况下,新文件区域的内容取决于具体平台(在大多数系统上,额外的字节会填充为零)。 返回新的文件大小。

    在 3.5 版更改: 现在Windows在扩展时将文件填充为零。

  • writable()

    如果流支持写入则返回 True。 如为 False,则 write()truncate() 将引发 OSError

  • writelines(lines)

    将行列表写入到流。 不会添加行分隔符,因此通常所提供的每一行都带有末尾行分隔符。

  • __del__()

    为对象销毁进行准备。 IOBase 提供了此方法的默认实现,该实现会调用实例的 close() 方法。

class io.RawIOBase

原始二进制流的基类。 它继承自 IOBase 类。 没有公共构造器。

原始二进制流通常会提供对下层 OS 设备或 API 的低层级访问,而不是尝试将其封装到高层级的基元中(此功能是在更高层级的缓冲二进制流和文本流中实现的,将在下文中描述)。

RawIOBaseIOBase 的现有成员以外还提供了下列方法:

  • read(size=- 1)

    从对象中读取 size 个字节并将其返回。 作为一个便捷选项,如果 size 未指定或为 -1,则返回所有字节直到 EOF。 在其他情况下,仅会执行一次系统调用。 如果操作系统调用返回字节数少于 size 则此方法也可能返回少于 size 个字节。

    如果返回 0 个字节而 size 不为零 0,这表明到达文件末尾。 如果处于非阻塞模式并且没有更多字节可用,则返回 None

    默认实现会转至 readall()readinto()

  • readall()

    从流中读取并返回所有字节直到 EOF,如有必要将对流执行多次调用。

  • readinto(b)

    将字节数据读入预先分配的可写 bytes-like object b,并返回所读取的字节数。 例如,b 可以是一个 bytearray。 如果对象处理非阻塞模式并且没有更多字节可用,则返回 None

  • write(b)

    将给定的 bytes-like object b 写入到下层的原始流,并返回所写入的字节数。 这可以少于 b 的总字节数,具体取决于下层原始流的设定,特别是如果它处于非阻塞模式的话。 如果原始流设为非阻塞并且不能真正向其写入单个字节时则返回 None。 调用者可以在此方法返回后释放或改变 b,因此该实现应该仅在方法调用期间访问 b

class io.BufferedIOBase

支持某种缓冲的二进制流的基类。 它继承自 IOBase。 没有公共构造器。

RawIOBase 的主要差别在于 read(), readinto()write() 等方法将(分别)尝试按照要求读取尽可能多的输入或是耗尽所有给定的输出,其代价是可能会执行一次以上的系统调用。

除此之外,那些方法还可能引发 BlockingIOError,如果下层的原始数据流处于非阻塞模式并且无法接受或给出足够数据的话;不同于对应的 RawIOBase 方法,它们将永远不会返回 None

并且,read() 方法也没有转向 readinto() 的默认实现。

典型的 BufferedIOBase 实现不应当继承自 RawIOBase 实现,而要包装一个该实现,正如 BufferedWriterBufferedReader 所做的那样。

BufferedIOBaseIOBase 的现有成员以外还提供或重载了下列数据属性和方法:

  • raw

    BufferedIOBase 处理的下层原始流 (RawIOBase 的实例)。 它不是 BufferedIOBase API 的组成部分并且不存在于某些实现中。

  • detach()

    从缓冲区分离出下层原始流并将其返回。

    在原始流被分离之后,缓冲区将处于不可用的状态。

    某些缓冲区例如 BytesIO 并无可从此方法返回的单独原始流的概念。 它们将会引发 UnsupportedOperation

    3.1 新版功能.

  • read(size=- 1)

    读取并返回最多 size 个字节。 如果此参数被省略、为 None 或为负值,则读取并返回所有数据直到 EOF。 如果流已经到达 EOF 则返回一个空的 bytes 对象。

    如果此参数为正值,并且下层原始流不可交互,则可能发起多个原始读取以满足字节计数(直至先遇到 EOF)。 但对于可交互原始流,则将至多发起一个原始读取,并且简短的结果并不意味着已到达 EOF。

    BlockingIOError 会在下层原始流不处于阻塞模式,并且当前没有可用数据时被引发。

  • read1([size])

    通过至多一次对下层流的 read() (或 readinto()) 方法的调用读取并返回至多 size 个字节。 这适用于在 BufferedIOBase 对象之上实现你自己的缓冲区的情况。

    如果 size-1 (默认值),则返回任意数量的字节(多于零字节,除非已到达 EOF)。

  • readinto(b)

    将字节数据读入预先分配的可写 bytes-like object b 并返回所读取的字节数。 例如,b 可以是一个 bytearray

    类似于 read(),可能对下层原始流发起多次读取,除非后者为交互式。

    BlockingIOError 会在下层原始流不处于阻塞模式,并且当前没有可用数据时被引发。

  • readinto1(b)

    将字节数据读入预先分配的可写 bytes-like object b,其中至多使用一次对下层原始流 read() (或 readinto()) 方法的调用。 返回所读取的字节数。

    BlockingIOError 会在下层原始流不处于阻塞模式,并且当前没有可用数据时被引发。

    3.5 新版功能.

  • write(b)

    写入给定的 bytes-like object b,并返回写入的字节数 (总是等于 b 的字节长度,因为如果写入失败则会引发 OSError)。 根据具体实现的不同,这些字节可能被实际写入下层流,或是出于运行效率和冗余等考虑而暂存于缓冲区。

    当处于非阻塞模式时,如果需要将数据写入原始流但它无法在不阻塞的情况下接受所有数据则将引发 BlockingIOError

    调用者可能会在此方法返回后释放或改变 b,因此该实现应当仅在方法调用期间访问 b

原始文件 I/O

class io.FileIO(name, mode=’r’, closefd=True, opener=None)

代表一个包含字节数据的 OS 层级文件的原始二进制流。 它继承自 RawIOBase

name 可以是以下两项之一:

  • 代表将被打开的文件路径的字符串或 bytes 对象。 在此情况下 closefd 必须为 True (默认值) 否则将会引发异常。
  • 代表一个现有 OS 层级文件描述符的号码的整数,作为结果的 FileIO 对象将可访问该文件。 当 FileIO 对象被关闭时此 fd 也将被关闭,除非 closefd 设为 False

mode 可以为 'r', 'w', 'x''a' 分别表示读取(默认模式)、写入、独占新建或添加。 如果以写入或添加模式打开的文件不存在将自动新建;当以写入模式打开时文件将先清空。 以新建模式打开时如果文件已存在则将引发 FileExistsError。 以新建模式打开文件也意味着要写入,因此该模式的行为与 'w' 类似。 在模式中附带 '+' 将允许同时读取和写入。

该类的 read() (当附带正值参数调用时), readinto()write() 方法将只执行一次系统调用。

可以通过传入一个可调用对象作为 opener 来使用自定义文件打开器。 然后通过调用 opener 并传入 (name, flags) 来获取文件对象所对应的下层文件描述符。 opener 必须返回一个打开文件描述符(传入 os.open 作为 opener 的结果在功能上将与传入 None 类似)。

新创建的文件是 不可继承的。

有关 opener 参数的示例,请参见内置函数 open()

在 3.3 版更改: 增加了 opener 参数。增加了 'x' 模式。

在 3.4 版更改: 文件现在禁止继承。

FileIO 在继承自 RawIOBaseIOBase 的现有成员以外还提供了以下数据属性和方法:

  • mode

    构造函数中给定的模式。

  • name

    文件名。当构造函数中没有给定名称时,这是文件的文件描述符。

缓冲流

相比原始 I/O,缓冲 I/O 流提供了针对 I/O 设备的更高层级接口。

class io.BytesIO([initial_bytes])

一个使用内在字节缓冲区的二进制流。 它继承自 BufferedIOBase。 在 close() 方法被调用时将会丢弃缓冲区。

可选参数 initial_bytes 是一个包含初始数据的 bytes-like object。

BytesIO 在继承自 BufferedIOBaseIOBase 的成员以外还提供或重载了下列方法:

  • getbuffer()

    返回一个对应于缓冲区内容的可读写视图而不必拷贝其数据。 此外,改变视图将透明地更新缓冲区内容:

    >>> b = io.BytesIO(b"abcdef")
    >>> view = b.getbuffer()
    >>> view[2:4] = b"56"
    >>> b.getvalue()
    b'ab56ef'

    注解

    只要视图保持存在,BytesIO 对象就无法被改变大小或关闭。

    3.2 新版功能.

  • getvalue()

    返回包含整个缓冲区内容的 bytes

  • read1([size])

    BytesIO 中,这与 read() 相同。

    在 3.7 版更改: size 参数现在是可选的。

  • readinto1(b)

    BytesIO 中,这与 readinto() 相同。

    3.5 新版功能.

class io.BufferedReader(raw, buffer_size=DEFAULT_BUFFER_SIZE)

一个提供对可读、不可查找的 RawIOBase 原始二进制流的高层级访问的缓冲二进制流。 它继承自 BufferedIOBase

当从此对象读取数据时,可能会从下层原始流请求更大量的数据,并存放到内部缓冲区中。 接下来可以在后续读取时直接返回缓冲数据。

根据给定的可读 raw 流和 buffer_size 创建 BufferedReader 的构造器。 如果省略 buffer_size,则会使用 DEFAULT_BUFFER_SIZE

BufferedReader 在继承自 BufferedIOBaseIOBase 的成员以外还提供或重载了下列方法:

  • peek([size])

    从流返回字节数据而不前移位置。 完成此调用将至多读取一次原始流。 返回的字节数量可能少于或多于请求的数量。

  • read([size])

    读取并返回 size 个字节,如果 size 未给定或为负值,则读取至 EOF 或是在非阻塞模式下读取调用将会阻塞。

  • read1([size])

    在原始流上通过单次调用读取并返回至多 size 个字节。 如果至少缓冲了一个字节,则只返回缓冲的字节。 在其他情况下,将执行一次原始流读取。

    在 3.7 版更改: size 参数现在是可选的。

class io.BufferedWriter(raw, buffer_size=DEFAULT_BUFFER_SIZE)

一个提供对可写、不可查找的 RawIOBase 原始二进制流的高层级访问的缓冲二进制流。 它继承自 BufferedIOBase

当写入到此对象时,数据通常会被放入到内部缓冲区中。 缓冲区将在满足某些条件的情况下被写到下层的 RawIOBase 对象,包括:

  • 当缓冲区对于所有挂起数据而言太小时;
  • flush() 被调用时
  • 当(为 BufferedRandom 对象)请求 seek() 时;
  • BufferedWriter 对象被关闭或销毁时。

该构造器会为给定的可写 raw 流创建一个 BufferedWriter。 如果未给定 buffer_size,则使用默认的 DEFAULT_BUFFER_SIZE

BufferedWriter 在继承自 BufferedIOBaseIOBase 的成员以外还提供或重载了下列方法:

  • flush()

    将缓冲区中保存的字节数据强制放入原始流。 如果原始流发生阻塞则应当引发 BlockingIOError

  • write(b)

    写入 bytes-like object b 并返回写入的字节数。 当处于非阻塞模式时,如果缓冲区需要被写入但原始流发生阻塞则将引发 BlockingIOError

class io.BufferedRandom(raw, buffer_size=DEFAULT_BUFFER_SIZE)

一个提供对不可查找的 RawIOBase 原始二进制流的高层级访问的缓冲二进制流。 它继承自 BufferedReaderBufferedWriter

该构造器会为在第一个参数中给定的可查找原始流创建一个读取器和定稿器。 如果省略 buffer_size 则使用默认的 DEFAULT_BUFFER_SIZE

BufferedRandom 能做到 BufferedReaderBufferedWriter 所能做的任何事。 此外,还会确保实现 seek()tell()

class io.BufferedRWPair(reader, writer, buffer_size=DEFAULT_BUFFER_SIZE)

一个提供对两个不可查找的 RawIOBase 原始二进制流的高层级访问的缓冲二进制流 —- 一个可读,另一个可写。 它继承自 BufferedIOBase

readerwriter 分别是可读和可写的 RawIOBase 对象。 如果省略 buffer_size 则使用默认的 DEFAULT_BUFFER_SIZE

BufferedRWPair 实现了 BufferedIOBase 的所有方法,但 detach() 除外,调用该方法将引发 UnsupportedOperation

警告

BufferedRWPair 不会尝试同步访问其下层的原始流。 你不应当将传给它与读取器和写入器相同的对象;而要改用 BufferedRandom

文本 I/O

class io.TextIOBase

文本流的基类。 该类提供了基于字符和行的流 I/O 接口。 它继承自 IOBase。 该类无公有构造器。

TextIOBase 在来自 IOBase 的成员以外还提供或重载了以下数据属性和方法:

  • encoding

    用于将流的字节串解码为字符串以及将字符串编码为字节串的编码格式名称。

  • errors

    解码器或编码器的错误设置。

  • newlines

    一个字符串、字符串元组或者 None,表示目前已经转写的新行。 根据具体实现和初始构造器旗标的不同,此属性或许会不可用。

  • buffer

    TextIOBase 处理的下层二进制缓冲区(为一个 BufferedIOBase 的实例)。 它不是 TextIOBase API 的组成部分并且不存在于某些实现中。

  • detach()

    TextIOBase 分离出下层二进制缓冲区并将其返回。

    在下层缓冲区被分离后,TextIOBase 将处于不可用的状态。

    某些 TextIOBase 的实现,例如 StringIO 可能并无下层缓冲区的概念,因此调用此方法将引发 UnsupportedOperation

    3.1 新版功能.

  • read(size=- 1)

    从流中读取至多 size 个字符并以单个 str 的形式返回。 如果 size 为负值或 None,则读取至 EOF。

  • readline(size=- 1)

    读取至换行符或 EOF 并返回单个 str。 如果流已经到达 EOF,则将返回一个空字符串。

    如果指定了 size ,最多将读取 size 个字符。

  • seek(offset, whence=SEEK_SET)

    将流位置改为给定的偏移位置 offset*。 具体行为取决于 *whence 形参。 whence 的默认值为 SEEK_SET

    • SEEK_SET0: 从流的开始位置起查找(默认值);offset 必须为 TextIOBase.tell() 所返回的数值或为零。 任何其他 offset 值都将导致未定义的行为。
    • SEEK_CUR1: “查找” 到当前位置;offset 必须为零,表示无操作(所有其他值均不受支持)。
    • SEEK_END2: 查找到流的末尾;offset 必须为零(所有其他值均不受支持)。

    以不透明数字形式返回新的绝对位置。

    3.1 新版功能: SEEK_* 常量.

  • tell()

    以不透明数字形式返回当前流的位置。 该数字通常并不代表下层二进制存储中对应的字节数。

  • write(s)

    将字符串 s 写入到流并返回写入的字符数。

class io.TextIOWrapper(buffer, encoding=None, errors=None, newline=None, line_buffering=False, write_through=False)

一个提供对 BufferedIOBase 缓冲二进制流的高层级访问的缓冲文本流。 它继承自 TextIOBase

encoding 给出流的解码和编码将会使用的编码格式的名称。 它默认为 locale.getpreferredencoding(False)encoding="locale" 可被用来地指定当前语言区域的编码格式。

errors 是一个可选的字符串,它指明编码格式和编码格式错误的处理方式。 传入 'strict' 将在出现编码格式错误时引发 ValueError (默认值 None 具有相同的效果),传入 'ignore' 将忽略错误。 (请注意忽略编码格式错误会导致数据丢失。) 'replace' 会在出现错误数据时插入一个替换标记 (例如 '?')。 'backslashreplace' 将把错误数据替换为一个反斜杠转义序列。 在写入时,还可以使用 'xmlcharrefreplace' (替换为适当的 XML 字符引用) 或 'namereplace' (替换为 \N{...} 转义序列)。 任何其他通过 codecs.register_error() 注册的错误处理方式名称也可以被接受。

newline 控制行结束符处理方式。 它可以为 None, '', '\n', '\r''\r\n'。 其工作原理如下:

  • 当从流读取输入时,如果 newlineNone,则将启用 universal newlines 模式。 输入中的行结束符可以为 '\n', '\r''\r\n',在返回给调用者之前它们会被统一转写为 '\n'。 如果 newline'',也会启用通用换行模式,但行结束符会不加转写即返回给调用者。 如果 newline 具有任何其他合法的值,则输入行将仅由给定的字符串结束,并且行结束符会不加转写即返回给调用者。
  • 将输出写入流时,如果 newlineNone,则写入的任何 '\n' 字符都将转换为系统默认行分隔符 os.linesep。如果 newline'''\n',则不进行翻译。如果 newline 是任何其他合法值,则写入的任何 '\n' 字符将被转换为给定的字符串。

如果 line_bufferingTrue,则当一个写入调用包含换行符或回车时将会应用 flush()

如果 write_throughTrue,对 write() 的调用会确保不被缓冲:在 TextIOWrapper 对象上写入的任何数据会立即交给其下层的 buffer 来处理。

在 3.3 版更改: 已添加 write_through 参数

在 3.3 版更改: 默认的 encoding 现在将为 locale.getpreferredencoding(False) 而非 locale.getpreferredencoding()。 不要使用 locale.setlocale() 来临时改变区域编码格式,要使用当前区域编码格式而不是用户的首选编码格式。

在 3.10 版更改: encoding 参数现在支持 "locale" 作为编码格式名称。

TextIOWrapper 在继承自 TextIOBaseIOBase 的现有成员以外还提供了以下数据属性和方法:

  • line_buffering

    是否启用行缓冲。

  • write_through

    写入是否要立即传给下层的二进制缓冲。

    3.7 新版功能.

  • reconfigure(\, encoding, newline[, write_through]*)

    使用 encoding, errors, newline, line_bufferingwrite_through 的新设置来重新配置此文本流。

    未指定的形参将保留当前设定,例外情况是当指定了 encoding 但未指定 errors 时将会使用 errors='strict'

    如果已经有数据从流中被读取则将无法再改变编码格式或行结束符。 另一方面,在写入数据之后再改变编码格式则是可以的。

    此方法会在设置新的形参之前执行隐式的流刷新。

    3.7 新版功能.

class io.StringIO(initial_value=’’, newline=’\n’)

一个使用内存文本缓冲的文本流。 它继承自 TextIOBase

close() 方法被调用时将会丢弃文本缓冲区。

缓冲区的初始值可通过提供 initial_value 来设置。 如果启用了行结束符转写,换行将以 write() 所用的方式被编码。 数据流位置将被设为缓冲区的开头。

newline 参数的规则与 TextIOWrapper 所用的一致,不同之处在于当将输出写入到流时,如果 newlineNone,则在所有平台上换行符都会被写入为 \n

StringIO 在继承自 TextIOBaseIOBase 的现有成员以外还提供了以下方法:

  • getvalue()

    返回一个包含缓冲区全部内容的 str。 换行符会以与 read() 相同的方式被编码,但是流的位置不会被改变。

用法示例:

import io
output = io.StringIO()
output.write('First line.\n')
print('Second line.', file=output)
# Retrieve file contents -- this will be
# 'First line.\nSecond line.\n'
contents = output.getvalue()
# Close object and discard memory buffer --
# .getvalue() will now raise an exception.
output.close()

class io.IncrementalNewlineDecoder

用于在 universal newlines 模式下解码换行符的辅助编解码器。 它继承自 codecs.IncrementalDecoder

性能

本节讨论所提供的具体 I/O 实现的性能。

二进制 I/O

即使在用户请求单个字节时,也只读取和写入大块数据。通过该方法,缓冲 I/O 隐藏了操作系统调用和执行无缓冲 I/O 例程时的任何低效性。增益取决于操作系统和执行的 I/O 类型。例如,在某些现代操作系统上(例如 Linux),无缓冲磁盘 I/O 可以与缓冲 I/O 一样快。但最重要的是,无论平台和支持设备如何,缓冲 I/O 都能提供可预测的性能。因此,对于二进制数据,应首选使用缓冲的 I/O 而不是未缓冲的 I/O 。

文本 I/O

二进制存储(如文件)上的文本 I/O 比同一存储上的二进制 I/O 慢得多,因为它需要使用字符编解码器在Unicode和二进制数据之间进行转换。这在处理大量文本数据(如大型日志文件)时会变得非常明显。此外,由于使用的重构算法 TextIOWrapper.tell()TextIOWrapper.seek() 都相当慢。

StringIO 是原生的内存 Unicode 容器,速度与 BytesIO 相似。

多线程

FileIO 对象是线程安全的,只要它们封装的操作系统调用(比如Unix下的 read(2) )也是线程安全的。

二进制缓冲对象(例如 BufferedReader, BufferedWriter, BufferedRandomBufferedRWPair)使用锁来保护其内部结构;因此,可以安全地一次从多个线程中调用它们。

TextIOWrapper 对象不再是线程安全的。

可重入性

二进制缓冲对象( BufferedReaderBufferedWriterBufferedRandomBufferedRWPair 的实例)不是可重入的。虽然在正常情况下不会发生可重入调用,但仍可能会在 signal 处理程序执行 I/O 时产生。如果线程尝试重入已经访问的缓冲对象,则会引发 RuntimeError 。注意,这并不禁止其他线程进入缓冲对象。

上面的内容隐式地扩展到文本文件中,因为 open() 函数将把缓冲对象封装在 TextIOWrapper 中。 这包括标准流,因而也会影响内置的 print() 函数。

time —- 时间的访问和转换

该模块提供了各种与时间相关的函数。相关功能还可以参阅 datetimecalendar 模块。

尽管所有平台皆可使用此模块,但模块内的函数并非所有平台都可用。此模块中定义的大多数函数的实现都是调用其所在平台的C语言库的同名函数。因为这些函数的语义可能因平台而异,所以使用时最好查阅对应平台的相关文档。

下面是一些术语和惯例的解释.

  • epoch 是时间开始的点,其值取决于平台。对于Unix, epoch 是1970年1月1日00:00:00(UTC)。要找出给定平台上的 epoch ,请查看 time.gmtime(0)

  • 术语 纪元秒数 是指自 epoch (纪元)时间点以来经过的总秒数,通常不包括 闰秒。 在所有符合 POSIX 标准的平台上,闰秒都不会记录在总秒数中。

  • 此模块中的函数可能无法处理纪元之前或遥远未来的日期和时间。“遥远未来”的定义由对应的C语言库决定;对于32位系统,它通常是指2038年及以后。

  • 函数 strptime() 在接收到 %y 格式代码时可以解析使用 2 位数表示的年份。当解析 2 位数年份时,函数会按照 POSIX 和 ISO C 标准进行年份转换:数值 69—99 被映射为 1969—1999;数值 0—68 被映射为 2000—2068。

  • UTC是协调世界时(Coordinated Universal Time)的缩写。它以前也被称为格林威治标准时间(GMT)。使用UTC而不是CUT作为缩写是英语与法语(Temps Universel Coordonné)之间妥协的结果,不是什么低级错误。

  • DST是夏令时(Daylight Saving Time)的缩写,在一年的某一段时间中将当地时间调整(通常)一小时。 DST的规则非常神奇(由当地法律确定),并且每年的起止时间都不同。C语言库中有一个表格,记录了各地的夏令时规则(实际上,为了灵活性,C语言库通常是从某个系统文件中读取这张表)。从这个角度而言,这张表是夏令时规则的唯一权威真理。

  • 由于平台限制,各种实时函数的精度可能低于其值或参数所要求(或给定)的精度。例如,在大多数Unix系统上,时钟频率仅为每秒50或100次。

  • 另一方面, time()sleep() 的精度优于它们的Unix等价物:时间表示为浮点数,time() 返回最准确的时间 (使用Unix gettimeofday() 如果可用),并且 sleep() 将接受非零分数的时间(Unix select() 用于实现此功能,如果可用)。

  • 时间值由 gmtime()localtime()strptime() 返回,并被 asctime()mktime()strftime() 接受,是一个 9 个整数的序列。 gmtime()localtime()strptime() 的返回值还提供各个字段的属性名称。

    在 3.3 版更改: 在平台支持相应的 struct tm 成员时,struct_time 类型被扩展提供 tm_gmtofftm_zone 属性。

    在 3.6 版更改: struct_time 的属性 tm_gmtofftm_zone 现在可在所有平台上使用。

  • 使用以下函数在时间表示之间进行转换:

    使用
    自纪元以来的秒数 UTC 的 struct_time gmtime()
    自纪元以来的秒数 本地时间的 struct_time localtime()
    UTC 的 struct_time 自纪元以来的秒数 calendar.timegm()
    本地时间的 struct_time 自纪元以来的秒数 mktime()

函数

time.asctime([t])

转换由 gmtime()localtime() 所返回的表示时间的元组或 struct_time 为以下形式的字符串: 'Sun Jun 20 23:21:05 1993'。 日期字段的长度为两个字符,如果日期只有一个数字则会以零填充,例如: 'Wed Jun 9 04:26:40 1993'

如果未提供 t,则会使用 localtime() 所返回的当前时间。 asctime() 不会使用区域设置信息。

注解

与同名的C函数不同, asctime() 不添加尾随换行符。

time.pthread_getcpuclockid(thread_id)

返回指定的 thread_id 的特定于线程的CPU时间时钟的 clk_id

使用 threading.Thread 对象的 threading.get_ident()ident 属性为 thread_id 获取合适的值。

警告

传递无效的或过期的 thread_id 可能会导致未定义的行为,例如段错误。

可用性 : Unix。

3.7 新版功能.

time.clock_getres(clk_id)

返回指定时钟 clk_id 的分辨率(精度)。

可用性: Unix。

3.3 新版功能.

time.clock_gettime(clk_id) → float

返回指定 clk_id 时钟的时间。

使用 clock_gettime_ns() 以避免 float 类型导致的精度损失。

可用性: Unix。

3.3 新版功能.

time.clock_gettime_ns(clk_id) → int

clock_gettime() 相似,但返回时间为纳秒。

可用性: Unix。

3.7 新版功能.

time.clock_settime(clk_id, time: float)

设置指定 clk_id 时钟的时间。 目前, CLOCK_REALTIMEclk_id 唯一可接受的值。

使用 clock_settime_ns() 以避免 float 类型导致的精度损失。

可用性: Unix。

3.3 新版功能.

time.clock_settime_ns(clk_id, time: int)

clock_settime() 相似,但设置时间为纳秒。

可用性: Unix。

3.7 新版功能.

time.ctime([secs])

转换以距离初始纪元的秒数表示的时间为以下形式的字符串: 'Sun Jun 20 23:21:05 1993' 代表本地时间。 日期字段的长度为两个字符,如果日期只有一个数字则会以零填充,例如: 'Wed Jun 9 04:26:40 1993'

如果 secs 未提供或为 None,则使用 time() 所返回的当前时间。 ctime(secs) 等价于 asctime(localtime(secs))ctime() 不会使用区域设置信息。

time.get_clock_info(name)

获取有关指定时钟的信息作为命名空间对象。 支持的时钟名称和读取其值的相应函数是:

  • 'monotonic': time.monotonic()
  • 'perf_counter': time.perf_counter()
  • 'process_time': time.process_time()
  • 'thread_time': time.thread_time()
  • 'time': time.time()

结果具有以下属性:

  • adjustable : 如果时钟可以自动更改(例如通过NTP守护程序)或由系统管理员手动更改,则为 True ,否则为 False
  • implementation : 用于获取时钟值的基础C函数的名称。
  • monotonic :如果时钟不能倒退,则为 True ,否则为 False
  • resolution : 以秒为单位的时钟分辨率( float

3.3 新版功能.

time.gmtime([secs])

将以自 epoch 开始的秒数表示的时间转换为 UTC 的 struct_time ,其中 dst 标志始终为零。 如果未提供 secs 或为 None ,则使用 time() 所返回的当前时间。 一秒以内的小数将被忽略。 有关 struct_time 对象的说明请参见上文。 有关此函数的逆操作请参阅 calendar.timegm()

time.localtime([secs])

gmtime() 相似但转换为当地时间。如果未提供 secs 或为 None ,则使用由 time() 返回的当前时间。当 DST 适用于给定时间时,dst标志设置为 1

time.mktime(t)

这是 localtime() 的反函数。它的参数是 struct_time 或者完整的 9 元组(因为需要 dst 标志;如果它是未知的则使用 -1 作为dst标志),它表示 local 的时间,而不是 UTC 。它返回一个浮点数,以便与 time() 兼容。如果输入值不能表示为有效时间,则 OverflowErrorValueError 将被引发(这取决于Python或底层C库是否捕获到无效值)。它可以生成时间的最早日期取决于平台。

time.monotonic() → float

(以小数表示的秒为单位)返回一个单调时钟的值,即不能倒退的时钟。 该时钟不受系统时钟更新的影响。 返回值的参考点未被定义,因此只有两次调用之间的差值才是有效的。

使用 monotonic_ns() 以避免 float 类型导致的精度损失。

3.3 新版功能.

在 3.5 版更改: 该功能现在始终可用且始终在系统范围内。

在 3.10 版更改: 在 macOS 上,现在这个函数作用于全系统。

time.monotonic_ns() → int

monotonic() 相似,但是返回时间为纳秒数。

3.7 新版功能.

time.perf_counter() → float

(以小数表示的秒为单位)返回一个性能计数器的值,即用于测量较短持续时间的具有最高有效精度的时钟。 它会包括睡眠状态所消耗的时间并且作用于全系统范围。 返回值的参考点未被定义,因此只有两次调用之间的差值才是有效的。

使用 perf_counter_ns() 以避免 float 类型导致的精度损失。

3.3 新版功能.

在 3.10 版更改: 在 Windows 上,现在这个函数作用于全系统。

time.perf_counter_ns() → int

perf_counter() 相似,但是返回时间为纳秒。

3.7 新版功能.

time.process_time() → float

(以小数表示的秒为单位)返回当前进程的系统和用户 CPU 时间的总计值。 它不包括睡眠状态所消耗的时间。 根据定义它只作用于进程范围。 返回值的参考点未被定义,因此只有两次调用之间的差值才是有效的。

使用 process_time_ns() 以避免 float 类型导致的精度损失。

3.3 新版功能.

time.process_time_ns() → int

process_time() 相似,但是返回时间为纳秒。

3.7 新版功能.

time.sleep(secs)

调用该方法的线程将被暂停执行 secs 秒。参数可以是浮点数,以表示更为精确的睡眠时长。由于任何捕获到的信号都会终止 sleep() 引发的该睡眠过程并开始执行信号的处理例程,因此实际的暂停时长可能小于请求的时长;此外,由于系统需要调度其他活动,实际暂停时长也可能比请求的时间长。

在 3.5 版更改: 现在,即使该睡眠过程被信号中断,该函数也会保证调用它的线程至少会睡眠 secs 秒。信号处理例程抛出异常的情况除外。(欲了解我们做出这次改变的原因,请参见 PEP 475

time.strftime(format[, t])

转换一个元组或 struct_time 表示的由 gmtime()localtime() 返回的时间到由 format 参数指定的字符串。如果未提供 t ,则使用由 localtime() 返回的当前时间。 format 必须是一个字符串。如果 t 中的任何字段超出允许范围,则引发 ValueError

0是时间元组中任何位置的合法参数;如果它通常是非法的,则该值被强制改为正确的值。

以下指令可以嵌入 format 字符串中。它们显示时没有可选的字段宽度和精度规范,并被 strftime() 结果中的指示字符替换:

指令 含意 备注
%a 本地化的缩写星期中每日的名称。
%A 本地化的星期中每日的完整名称。
%b 本地化的月缩写名称。
%B 本地化的月完整名称。
%c 本地化的适当日期和时间表示。
%d 十进制数 [01,31] 表示的月中日。
%H 十进制数 [00,23] 表示的小时(24小时制)。
%I 十进制数 [01,12] 表示的小时(12小时制)。
%j 十进制数 [001,366] 表示的年中日。
%m 十进制数 [01,12] 表示的月。
%M 十进制数 [00,59] 表示的分钟。
%p 本地化的 AM 或 PM 。 (1)
%S 十进制数 [00,61] 表示的秒。 (2)
%U 十进制数 [00,53] 表示的一年中的周数(星期日作为一周的第一天)。 在第一个星期日之前的新年中的所有日子都被认为是在第 0 周。 (3)
%w 十进制数 [0(星期日),6] 表示的周中日。
%W 十进制数 [00,53] 表示的一年中的周数(星期一作为一周的第一天)。 在第一个星期一之前的新年中的所有日子被认为是在第 0 周。 (3)
%x 本地化的适当日期表示。
%X 本地化的适当时间表示。
%y 十进制数 [00,99] 表示的没有世纪的年份。
%Y 十进制数表示的带世纪的年份。
%z 时区偏移以格式 +HHMM 或 -HHMM 形式的 UTC/GMT 的正或负时差指示,其中H表示十进制小时数字,M表示小数分钟数字 [-23:59, +23:59] 。
%Z 时区名称(如果不存在时区,则不包含字符)。
%% 字面的 ‘%’ 字符。

注释:

  1. 当与 strptime() 函数一起使用时,如果使用 %I 指令来解析小时, %p 指令只影响输出小时字段。
  2. 范围真的是 061 ;值 60 在表示 leap seconds 的时间戳中有效,并且由于历史原因支持值 61
  3. 当与 strptime() 函数一起使用时, %U%W 仅用于指定星期几和年份的计算。

下面是一个示例,一个与 RFC 2822 Internet电子邮件标准以兼容的日期格式.

>>> from time import gmtime, strftime
>>> strftime("%a, %d %b %Y %H:%M:%S +0000", gmtime())
'Thu, 28 Jun 2001 14:17:15 +0000'

某些平台可能支持其他指令,但只有此处列出的指令具有 ANSI C 标准化的含义。

在某些平台上,可选的字段宽度和精度规范可以按照以下顺序紧跟在指令的初始 '%' 之后;这也不可移植。字段宽度通常为2,除了 %j ,它是3。

time.strptime(string[, format])

根据格式解析表示时间的字符串。 返回值为一个被 gmtime()localtime() 返回的 struct_time

format 参数使用与 strftime() 相同的指令。 它默认为匹配 ctime() 所返回的格式 "%a %b %d %H:%M:%S %Y" 。 如果 string 不能根据 format 来解析,或者解析后它有多余的数据,则会引发 ValueError。 当无法推断出更准确的值时,用于填充任何缺失数据的默认值是 (1900, 1, 1, 0, 0, 0, 0, 1, -1)stringformat 都必须为字符串。

例如:

>>> import time
>>> time.strptime("30 Nov 00", "%d %b %y")   
time.struct_time(tm_year=2000, tm_mon=11, tm_mday=30, tm_hour=0, tm_min=0,
                 tm_sec=0, tm_wday=3, tm_yday=335, tm_isdst=-1)

支持 %Z 指令是基于 tzname 中包含的值以及 daylight 是否为真。因此,它是特定于平台的,除了识别始终已知的 UTC 和 GMT (并且被认为是非夏令时时区)。

仅支持文档中指定的指令。因为每个平台都实现了 strftime() ,它有时会提供比列出的指令更多的指令。但是 strptime() 独立于任何平台,因此不一定支持所有未记录为支持的可用指令。

class time.struct_time

返回的时间值序列的类型为 gmtime()localtime()strptime() 。它是一个带有 named tuple 接口的对象:可以通过索引和属性名访问值。 存在以下值:

索引 属性
0 tm_year (例如,1993)
1 tm_mon range [1, 12]
2 tm_mday range [1, 31]
3 tm_hour range [0, 23]
4 tm_min range [0, 59]
5 tm_sec range [0, 61];
6 tm_wday range [0, 6] ,周一为 0
7 tm_yday range [1, 366]
8 tm_isdst 0, 1 或 -1;如下所示
N/A tm_zone 时区名称的缩写
N/A tm_gmtoff 以秒为单位的UTC以东偏离

请注意,与C结构不同,月份值是 [1,12] 的范围,而不是 [0,11] 。

在调用 mktime() 时, tm_isdst 可以在夏令时生效时设置为1,而在夏令时不生效时设置为0。 值-1表示这是未知的,并且通常会导致填写正确的状态。

当一个长度不正确的元组被传递给期望 struct_time 的函数,或者具有错误类型的元素时,会引发 TypeError

time.time() → float

返回以浮点数表示的从 epoch 开始的秒数的时间值。 epoch 的具体日期和 leap seconds 的处理取决于平台。 在 Windows 和大多数 Unix 系统中, epoch 是 1970 年 1 月 1 日 00:00:00 (UTC),并且闰秒将不计入从 epoch 开始的秒数。 这通常被称为 Unix 时间。 要了解给定平台上 epoch 的具体定义,请查看 gmtime(0)

请注意,即使时间总是作为浮点数返回,但并非所有系统都提供高于1秒的精度。虽然此函数通常返回非递减值,但如果在两次调用之间设置了系统时钟,则它可以返回比先前调用更低的值。

返回的数字 time() 可以通过将其传递给 gmtime() 函数或转换为UTC中更常见的时间格式(即年、月、日、小时等)或通过将它传递给 localtime() 函数获得本地时间。在这两种情况下都返回一个 struct_time 对象,日历日期组件可以从中作为属性访问。

使用 time_ns() 以避免 float 类型导致的精度损失。

time.time_ns() → int

time() 相似,但返回时间为用整数表示的自 epoch 以来所经过的纳秒数。

3.7 新版功能.

time.thread_time() → float

(以小数表示的秒为单位)返回当前线程的系统和用户 CPU 时间的总计值。 它不包括睡眠状态所消耗的时间。 根据定义它只作用于线程范围。 返回值的参考点未被定义,因此只有两次调用之间的差值才是有效的。

使用 thread_time_ns() 以避免 float 类型导致的精度损失。

可用性 : Windows、 Linux、 Unix 系统支持 CLOCK_THREAD_CPUTIME_ID

3.7 新版功能.

time.thread_time_ns() → int

thread_time() 相似,但返回纳秒时间。

3.7 新版功能.

time.tzset()

重置库例程使用的时间转换规则。环境变量 TZ 指定如何完成。它还将设置变量 tzname (来自 TZ 环境变量), timezone (UTC的西部非DST秒), altzone (UTC以西的DST秒)和 daylight (如果此时区没有任何夏令时规则则为0,如果有夏令时适用的时间,无论过去、现在或未来,则为非零)。

可用性: Unix。

注解

虽然在很多情况下,更改 TZ 环境变量而不调用 tzset() 可能会影响函数的输出,例如 localtime() ,不应该依赖此行为。

TZ 不应该包含空格。

TZ 环境变量的标准格式是(为了清晰起见,添加了空格):

std offset [dst [offset [,start[/time], end[/time]]]]

组件的位置是:

  • stddst

    三个或更多字母数字,给出时区缩写。这些将传到 time.tzname

    offset

    偏移量的形式为: ± hh[:mm[:ss]] 。这表示添加到达UTC的本地时间的值。如果前面有 ‘-‘ ,则时区位于本初子午线的东边;否则,在它是西边。如果dst之后没有偏移,则假设夏令时比标准时间提前一小时。

    start[/time], end[/time]

    指示何时更改为DST和从DST返回。开始日期和结束日期的格式为以下之一:

    • J*n*

      Julian日 n (1 <= n <= 365)。闰日不计算在内,因此在所有年份中,2月28日是第59天,3月1日是第60天。

      *n*

      从零开始的Julian日(0 <= n <= 365)。 闰日计入,可以引用2月29日。

      M*m*.*n*.*d*

      一年中 m 月的第 n 周(1 <= n <= 5 ,1 <= m <= 12 ,第 5 周表示 “可能在 m 月第 4 周或第 5 周出现的最后第 d 日”)的第 d 天(0 <= d <= 6)。 第 1 周是第 d 天发生的第一周。 第 0 天是星期天。

    time 的格式与 offset 的格式相同,但不允许使用前导符号( ‘-‘ 或 ‘+’ )。如果没有给出时间,则默认值为02:00:00。

>>> os.environ['TZ'] = 'EST+05EDT,M4.1.0,M10.5.0'
>>> time.tzset()
>>> time.strftime('%X %x %Z')
'02:07:36 05/08/03 EDT'
>>> os.environ['TZ'] = 'AEST-10AEDT-11,M10.5.0,M3.5.0'
>>> time.tzset()
>>> time.strftime('%X %x %Z')
'16:08:12 05/08/03 AEST'

在许多Unix系统(包括 *BSD , Linux , Solaris 和 Darwin 上),使用系统的区域信息数据库来指定时区规则会更方便。为此,将 TZ 环境变量设置为所需时区数据文件的路径,相对于系统 ‘zoneinfo’ 时区数据库的根目录,通常位于 /usr/share/zoneinfo 。 例如,'US/Eastern''Australia/Melbourne''Egypt''Europe/Amsterdam'

>>> os.environ['TZ'] = 'US/Eastern'
>>> time.tzset()
>>> time.tzname
('EST', 'EDT')
>>> os.environ['TZ'] = 'Egypt'
>>> time.tzset()
>>> time.tzname
('EET', 'EEST')

Clock ID 常量

这些常量用作 clock_getres()clock_gettime() 的参数。

time.CLOCK_BOOTTIME

CLOCK_MONOTONIC 相同,除了它还包括系统暂停的任何时间。

这允许应用程序获得一个暂停感知的单调时钟,而不必处理 CLOCK_REALTIME 的复杂性,如果使用 settimeofday() 或类似的时间更改时间可能会有不连续性。

可用性: Linux 2.6.39 或更新

3.7 新版功能.

time.CLOCK_HIGHRES

Solaris OS 有一个 CLOCK_HIGHRES 计时器,试图使用最佳硬件源,并可能提供接近纳秒的分辨率。 CLOCK_HIGHRES 是不可调节的高分辨率时钟。

可用性: Solaris.

3.3 新版功能.

time.CLOCK_MONOTONIC

无法设置的时钟,表示自某些未指定的起点以来的单调时间。

可用性: Unix。

3.3 新版功能.

time.CLOCK_MONOTONIC_RAW

类似于 CLOCK_MONOTONIC ,但可以访问不受NTP调整影响的原始硬件时间。

可用性: Linux 2.6.28 和更新版本, macOS 10.12 和更新版本。

3.3 新版功能.

time.CLOCK_PROCESS_CPUTIME_ID

来自CPU的高分辨率每进程计时器。

可用性: Unix。

3.3 新版功能.

time.CLOCK_PROF

来自CPU的高分辨率每进程计时器。

可用性: FreeBSD, NetBSD 7 或更新, OpenBSD.

3.7 新版功能.

time.CLOCK_TAI

国际原子时间

该系统必须有一个当前闰秒表以便能给出正确的回答。 PTP 或 NTP 软件可以用来维护闰秒表。

可用性: Linux。

3.9 新版功能.

time.CLOCK_THREAD_CPUTIME_ID

特定于线程的CPU时钟。

可用性: Unix。

3.3 新版功能.

time.CLOCK_UPTIME

该时间的绝对值是系统运行且未暂停的时间,提供准确的正常运行时间测量,包括绝对值和间隔值。

可用性: FreeBSD, OpenBSD 5.5 或更新。

3.7 新版功能.

time.CLOCK_UPTIME_RAW

单调递增的时钟,记录从一个任意起点开始的时间,不受频率或时间调整的影响,并且当系统休眠时将不会递增。

可用性: macOS 10.12 和更新版本。

3.8 新版功能.

以下常量是唯一可以发送到 clock_settime() 的参数。

time.CLOCK_REALTIME

系统范围的实时时钟。 设置此时钟需要适当的权限。

可用性: Unix。

3.3 新版功能.

时区常量

time.altzone

本地DST时区的偏移量,以UTC为单位的秒数,如果已定义。如果当地DST时区在UTC以东(如在西欧,包括英国),则是负数。 只有当 daylight 非零时才使用它。 见下面的注释。

time.daylight

如果定义了DST时区,则为非零。 见下面的注释。

time.timezone

本地(非DST)时区的偏移量,UTC以西的秒数(西欧大部分地区为负,美国为正,英国为零)。 见下面的注释。

time.tzname

两个字符串的元组:第一个是本地非DST时区的名称,第二个是本地DST时区的名称。 如果未定义DST时区,则不应使用第二个字符串。 见下面的注释。

注解

对于上述时区常量( altzonedaylighttimezonetzname ),该值由模块加载时有效的时区规则确定,或者最后一次 tzset() 被调用时,并且在过去的时间可能不正确。建议使用来自 localtime() 结果的 tm_gmtofftm_zone 来获取时区信息。

getopt —- C 风格的命令行选项解析器

源代码: Lib/getopt.py

注解

getopt 模块是一个命令行选项解析器,其 API 设计会让 C getopt() 函数的用户感到熟悉。 不熟悉 C getopt() 函数或者希望写更少代码并获得更完善帮助和错误消息的用户应当考虑改用 argparse 模块。


此模块可协助脚本解析 sys.argv 中的命令行参数。 它支持与 Unix getopt() 函数相同的惯例(包括形式如 ‘-‘ 与 ‘--‘ 的参数的特殊含义)。 也能通过可选的第三个参数来使用与 GNU 软件所支持形式相类似的长选项。

此模块提供了两个函数和一个异常:

getopt.getopt(args, shortopts, longopts=[])

解析命令行选项与形参列表。 args 为要解析的参数列表,不包含最开头的对正在运行的程序的引用。 通常这意味着 sys.argv[1:]shortopts 为脚本所要识别的字母选项,包含要求后缀一个冒号 (':';即与 Unix getopt() 所用的格式相同) 的选项。

注解

与 GNU getopt() 不同,在非选项参数之后,所有后续参数都会被视为非选项。 这类似于非 GNU Unix 系统的运作方式。

如果指定了 longopts*,则必须为一个由应当被支持的长选项名称组成的列表。 开头的 '--' 字符不应被包括在选项名称中。 要求参数的长选项后应当带一个等号 ('=')。 可选参数不被支持。 如果想仅接受长选项,则 *shortopts 应为一个空字符串。 命令行中的长选项只要提供了恰好能匹配可接受选项之一的选项名称前缀即可被识别。 举例来说,如果 longopts['foo', 'frob'],则选项 --fo 将匹配为 --foo,但 --f 将不能得到唯一匹配,因此将引发 GetoptError

返回值由两个元素组成:第一个是 (option, value) 对的列表;第二个是在去除该选项列表后余下的程序参数列表(这也就是 args 的尾部切片)。每个被返回的选项与值对的第一个元素是选项,短选项前缀一个连字符 (例如 '-x'),长选项则前缀两个连字符 (例如 '--long-option'),第二个元素是选项参数,如果选项不带参数则为空字符串。 列表中选项的排列顺序与它们被解析的顺序相同,因此允许多次出现。 长选项与短选项可以混用。

getopt.gnu_getopt(args, shortopts, longopts=[])

此函数与 getopt() 类似,区别在于它默认使用 GNU 风格的扫描模式。 这意味着选项和非选项参数可能会混在一起。 getopt() 函数将在遇到非选项参数时立即停止处理选项。

如果选项字符串的第一个字符为 '+',或者如果设置了环境变量 POSIXLY_CORRECT,则选项处理会在遇到非选项参数时立即停止。

exception getopt.GetoptError

This is raised当参数列表中出现不可识别的选项或者当一个需要参数的选项未带参数时将引发此异常。 此异常的参数是一个指明错误原因的字符串。 对于长选项,将一个参数传给不需要参数的选项也将导致引发此异常。 msgopt 属性会给出错误消息和关联的选项;如果没有关联到异常的特定选项,则 opt 将为空字符串。

exception getopt.error

GetoptError 的别名;用于向后兼容。

一个仅使用 Unix 风格选项的例子:

>>> import getopt
>>> args = '-a -b -cfoo -d bar a1 a2'.split()
>>> args
['-a', '-b', '-cfoo', '-d', 'bar', 'a1', 'a2']
>>> optlist, args = getopt.getopt(args, 'abc:d:')
>>> optlist
[('-a', ''), ('-b', ''), ('-c', 'foo'), ('-d', 'bar')]
>>> args
['a1', 'a2']

使用长选项名也同样容易:

>>> s = '--condition=foo --testing --output-file abc.def -x a1 a2'
>>> args = s.split()
>>> args
['--condition=foo', '--testing', '--output-file', 'abc.def', '-x', 'a1', 'a2']
>>> optlist, args = getopt.getopt(args, 'x', [
...     'condition=', 'output-file=', 'testing'])
>>> optlist
[('--condition', 'foo'), ('--testing', ''), ('--output-file', 'abc.def'), ('-x', '')]
>>> args
['a1', 'a2']

在脚本中,典型的用法类似这样:

import getopt, sys
def main():
    try:
        opts, args = getopt.getopt(sys.argv[1:], "ho:v", ["help", "output="])
    except getopt.GetoptError as err:
        # print help information and exit:
        print(err)  # will print something like "option -a not recognized"
        usage()
        sys.exit(2)
    output = None
    verbose = False
    for o, a in opts:
        if o == "-v":
            verbose = True
        elif o in ("-h", "--help"):
            usage()
            sys.exit()
        elif o in ("-o", "--output"):
            output = a
        else:
            assert False, "unhandled option"
    # ...
if __name__ == "__main__":
    main()

请注意通过 argparse 模块可以使用更少的代码并附带更详细的帮助与错误消息生成等价的命令行接口:

import argparse
if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('-o', '--output')
    parser.add_argument('-v', dest='verbose', action='store_true')
    args = parser.parse_args()
    # ... do something with args.output ...
    # ... do something with args.verbose ..

argparse —- 命令行选项、参数和子命令解析器

3.2 新版功能.

源代码:Lib/argparse.py

模块解析:Python-Argparse

logging —- Python 的日志记录工具

源代码: Lib/logging/init.py

Important

模块解析:Python-Logging

这个模块为应用与库实现了灵活的事件日志系统的函数与类。

使用标准库提供的 logging API 最主要的好处是,所有的 Python 模块都可能参与日志输出,包括你自己的日志消息和第三方模块的日志消息。

这个模块提供许多强大而灵活的功能。如果你对 logging 不太熟悉的话, 掌握它最好的方式就是查看它对应的教程(详见右侧的链接)。

该模块定义的基础类和函数都列在下面。

  • 记录器暴露了应用程序代码直接使用的接口。
  • 处理器将日志记录(由记录器创建)发送到适当的目标。
  • 过滤器提供了更细粒度的功能,用于确定要输出的日志记录。
  • 格式器指定最终输出中日志记录的样式。

记录器对象

记录器有以下的属性和方法。注意 永远 不要直接实例化记录器,应当通过模块级别的函数 logging.getLogger(name) 。多次使用相同的名字调用 getLogger() 会一直返回相同的 Logger 对象的引用。

name 一般是句点分割的层级值, 像foo.bar.baz (尽管也可以只是普通的 foo)。层次结构列表中位于下方的记录器是列表中较高位置的记录器的子级。例如,有个名叫 foo 的记录器,而名字是 foo.barfoo.bar.baz,和 foo.bam 的记录器都是 foo 的子级。记录器的名字分级类似 Python 包的层级,如果您使用建议的结构 logging.getLogger(__name__) 在每个模块的基础上组织记录器,则与之完全相同。这是因为在模块里,__name__ 是该模块在 Python 包命名空间中的名字。

class logging.Logger

  • propagate

    如果这个属性为真,记录到这个记录器的事件除了会发送到此记录器的所有处理程序外,还会传递给更高级别(祖先)记录器的处理器,此外任何关联到这个记录器的处理器。消息会直接传递给祖先记录器的处理器 —— 不考虑祖先记录器的级别和过滤器。

    如果为假,记录消息将不会传递给当前记录器的祖先记录器的处理器。

    构造器将这个属性初始化为 True

    注解

    如果你将一个处理器附加到一个记录器 其一个或多个祖先记录器,它可能发出多次相同的记录。通常,您不需要将一个处理器附加到一个以上的记录器上 —— 如果您将它附加到记录器层次结构中最高的适当记录器上,则它将看到所有后代记录器记录的所有事件,前提是它们的传播设置保留为 True。一种常见的方案是仅将处理器附加到根记录器,通过传播来处理其余部分。

  • setLevel(level)

    给记录器设置阈值为 level 。日志等级小于 level 会被忽略。严重性为 level 或更高的日志消息将由该记录器的任何一个或多个处理器发出,除非将处理器的级别设置为比 level 更高的级别。

    创建记录器时,级别默认设置为 NOTSET (当记录器是根记录器时,将处理所有消息;如果记录器不是根记录器,则将委托给父级)。请注意,根记录器的默认级别为 WARNING

    委派给父级的意思是如果一个记录器的级别设置为 NOTSET,将遍历其祖先记录器,直到找到级别不是 NOTSET 的记录器,或者到根记录器为止。

    如果发现某个父级的级别不是 NOTSET ,那么该父级的级别将被视为发起搜索的记录器的有效级别,并用于确定如何处理日志事件。

    如果搜索到达根记录器,并且其级别为 NOTSET,则将处理所有消息。否则,将使用根记录器的级别作为有效级别。

    在 3.2 版更改: 现在 level 参数可以接受形如 ‘INFO’ 的级别字符串表示形式,以代替形如 INFO 的整数常量。 但是请注意,级别在内部存储为整数,并且 getEffectiveLevel()isEnabledFor() 等方法的传入/返回值也为整数。

  • isEnabledFor(level)

    指示此记录器是否将处理级别为 level 的消息。此方法首先检查由 logging.disable(level) 设置的模块级的级别,然后检查由 getEffectiveLevel() 确定的记录器的有效级别。

  • getEffectiveLevel()

    指示此记录器的有效级别。如果通过 setLevel() 设置了除 NOTSET 以外的值,则返回该值。否则,将层次结构遍历到根,直到找到除 NOTSET 以外的其他值,然后返回该值。返回的值是一个整数,通常为 logging.DEBUGlogging.INFO 等等。

  • getChild(suffix)

    返回由后缀确定的该记录器的后代记录器。 因此,logging.getLogger('abc').getChild('def.ghi')logging.getLogger('abc.def.ghi') 将返回相同的记录器。 这是一个便捷方法,当使用如 __name__ 而不是字符串字面值命名父记录器时很有用。

    3.2 新版功能.

  • debug(msg, \args, *kwargs)

    在此记录器上记录 DEBUG 级别的消息。 msg 是消息格式字符串,而 args 是用于字符串格式化操作合并到 msg 的参数。(请注意,这意味着您可以在格式字符串中使用关键字以及单个字典参数。)当未提供 args 时,不会对 msg 执行 % 格式化操作。

    kwargs 中会检查四个关键字参数: exc_infostack_infostacklevelextra

    如果 exc_info 的求值结果不为 false ,则它将异常信息添加到日志消息中。如果提供了一个异常元组(按照 sys.exc_info() 返回的格式)或一个异常实例,则它将被使用;否则,调用 sys.exc_info() 以获取异常信息。

    第二个可选关键字参数是 stack_info*,默认为 False。如果为 True,则将堆栈信息添加到日志消息中,包括实际的日志调用。请注意,这与通过指定 *exc_info 显示的堆栈信息不同:前者是从堆栈底部到当前线程中的日志记录调用的堆栈帧,而后者是在搜索异常处理程序时,跟踪异常而打开的堆栈帧的信息。

    您可以独立于 exc_info 来指定 stack_info,例如,即使在未引发任何异常的情况下,也可以显示如何到达代码中的特定点。堆栈帧在标题行之后打印:

    Stack (most recent call last):

    这模仿了显示异常帧时所使用的 Traceback (most recent call last):

    第三个可选关键字参数是 stacklevel ,默认为 1 。如果大于 1 ,则在为日志记录事件创建的 LogRecord 中计算行号和函数名时,将跳过相应数量的堆栈帧。可以在记录帮助器时使用它,以便记录的函数名称,文件名和行号不是帮助器的函数/方法的信息,而是其调用方的信息。此参数是 warnings 模块中的同名等效参数。

    第四个关键字参数是 extra ,传递一个字典,该字典用于填充为日志记录事件创建的、带有用户自定义属性的 LogRecord 中的 dict 。然后可以按照需求使用这些自定义属性。例如,可以将它们合并到已记录的消息中:

    FORMAT = '%(asctime)-15s %(clientip)s %(user)-8s %(message)s'
    logging.basicConfig(format=FORMAT)
    d = {'clientip': '192.168.0.1', 'user': 'fbloggs'}
    logger = logging.getLogger('tcpserver')
    logger.warning('Protocol problem: %s', 'connection reset', extra=d)

    输出类似于

    2006-02-08 22:20:02,165 192.168.0.1 fbloggs  Protocol problem: connection reset

    extra 中传入的字典的键不应与日志系统使用的键冲突。

    如果在已记录的消息中使用这些属性,则需要格外小心。例如,在上面的示例中,Formatter 已设置了格式字符串,其在 LogRecord 的属性字典中键值为 “clientip” 和 “user”。如果缺少这些内容,则将不会记录该消息,因为会引发字符串格式化异常。因此,在这种情况下,您始终需要使用 extra 字典传递这些键。

    尽管这可能很烦人,但此功能旨在用于特殊情况,例如在多个上下文中执行相同代码的多线程服务器,并且出现的有趣条件取决于此上下文(例如在上面的示例中就是远程客户端IP地址和已验证用户名)。在这种情况下,很可能将专门的 Formatter 与特定的 Handler 一起使用。

    在 3.2 版更改: 增加了 stack_info 参数。

    在 3.5 版更改: exc_info 参数现在可以接受异常实例。

    在 3.8 版更改: 增加了 stacklevel 参数。

  • info(msg, \args, *kwargs)

    在此记录器上记录 INFO 级别的消息。

  • warning(msg, \args, *kwargs)

    在此记录器上记录 WARNING 级别的消息。

    注解

    有一个功能上与 warning 一致的方法 warn。由于 warn 已被弃用,请不要使用它 —— 改为使用 warning

  • error(msg, \args, *kwargs)

    在此记录器上记录 ERROR 级别的消息。

  • critical(msg, \args, *kwargs)

    在此记录器上记录 CRITICAL 级别的消息。

  • log(level, msg, \args, *kwargs)

    在此记录器上记录 level 整数代表的级别的消息。

  • exception(msg, \args, *kwargs)

    在此记录器上记录 ERROR 级别的消息。异常信息将添加到日志消息中。仅应从异常处理程序中调用此方法。

  • addFilter(filter)

    将指定的过滤器 filter 添加到此记录器。

  • removeFilter(filter)

    从此记录器中删除指定的过滤器 filter

  • filter(record)

    将此记录器的过滤器应用于记录,如果记录能被处理则返回 True。过滤器会被依次使用,直到其中一个返回假值为止。如果它们都不返回假值,则记录将被处理(传递给处理器)。如果返回任一为假值,则不会对该记录做进一步处理。

  • addHandler(hdlr)

    将指定的处理器 hdlr 添加到此记录器。

  • removeHandler(hdlr)

    从此记录器中删除指定的处理器 hdlr

  • findCaller(stack_info=False, stacklevel=1)

    查找调用源的文件名和行号,以 文件名,行号,函数名称和堆栈信息 4元素元组的形式返回。堆栈信息将返回 None,除非 stack_infoTrue

    stacklevel 参数用于调用 debug() 和其他 API。如果大于 1,则多余部分将用于跳过堆栈帧,然后再确定要返回的值。当从帮助器/包装器代码调用日志记录 API 时,这通常很有用,以便事件日志中的信息不是来自帮助器/包装器代码,而是来自调用它的代码。

  • handle(record)

    通过将记录传递给与此记录器及其祖先关联的所有处理器来处理(直到某个 propagate 值为 false)。此方法用于从套接字接收的未序列化的以及在本地创建的记录。使用 filter() 进行记录器级别过滤。

  • makeRecord(name, level, fn, lno, msg, args, exc_info, func=None, extra=None, sinfo=None)

    这是一种工厂方法,可以在子类中对其进行重写以创建专门的 LogRecord 实例。

  • hasHandlers()

    检查此记录器是否配置了任何处理器。通过在此记录器及其记录器层次结构中的父级中查找处理器完成此操作。如果找到处理器则返回 True,否则返回 False。只要找到 “propagate” 属性设置为假值的记录器,该方法就会停止搜索层次结构 —— 其将是最后一个检查处理器是否存在的记录器。

    3.2 新版功能.

在 3.7 版更改: 现在可以对处理器进行序列化和反序列化。

日志级别

日志记录级别的数值在下表中给出。如果你想要定义自己的级别,并且需要它们具有相对于预定义级别的特定值,那么这你可能对以下内容感兴趣。如果你定义具有相同数值的级别,它将覆盖预定义的值;预定义的名称将失效。

级别 数值
CRITICAL 50
ERROR 40
WARNING 30
INFO 20
DEBUG 10
NOTSET 0

处理器对象

Handler 有以下属性和方法。注意不要直接实例化 Handler ;这个类用来派生其他更有用的子类。但是,子类的 __init__() 方法需要调用 Handler.__init__()

class logging.Handler

  • __init__(level=NOTSET)

    初始化 Handler 实例时,需要设置它的级别,将过滤列表置为空,并且创建锁(通过 createLock() )来序列化对 I/O 的访问。

  • createLock()

    初始化一个线程锁,用来序列化对底层的 I/O 功能的访问,底层的 I/O 功能可能不是线程安全的。

  • acquire()

    获取由 createLock() 创建的线程锁。

  • release()

    释放由 acquire() 获取的线程锁。

  • setLevel(level)

    给处理器设置阈值为 level 。日志级别小于 level 将被忽略。创建处理器时,日志级别被设置为 NOTSET (所有的消息都会被处理)。

    在 3.2 版更改: level 形参现在接受像 ‘INFO’ 这样的字符串形式的级别表达方式,也可以使用像 INFO 这样的整数常量。

  • setFormatter(fmt)

    将此处理器的 Formatter 设置为 fmt

  • addFilter(filter)

    将指定的过滤器 filter 添加到此处理器。

  • removeFilter(filter)

    从此处理器中删除指定的过滤器 filter

  • filter(record)

    将此处理器的过滤器应用于记录,在要处理记录时返回 True 。依次查询过滤器,直到其中一个返回假值为止。如果它们都不返回假值,则将发出记录。如果返回一个假值,则处理器将不会发出记录。

  • flush()

    确保所有日志记录从缓存输出。此版本不执行任何操作,并且应由子类实现。

  • close()

    回收处理器使用的所有资源。此版本不输出,但从内部处理器列表中删除处理器,内部处理器在 shutdown() 被调用时关闭 。子类应确保从重写的 close() 方法中调用此方法。

  • handle(record)

    经已添加到处理器的过滤器过滤后,有条件地发出指定的日志记录。用获取/释放 I/O 线程锁包装了记录的实际发出行为。

  • handleError(record)

    调用 emit() 期间遇到异常时,应从处理器中调用此方法。如果模块级属性 raiseExceptionsFalse,则异常将被静默忽略。这是大多数情况下日志系统需要的 —— 大多数用户不会关心日志系统中的错误,他们对应用程序错误更感兴趣。但是,你可以根据需要将其替换为自定义处理器。指定的记录是发生异常时正在处理的记录。(raiseExceptions 的默认值是 True,因为这在开发过程中是比较有用的)。

  • format(record)

    如果设置了格式器则用其对记录进行格式化。否则,使用模块的默认格式器。

  • emit(record)

    执行实际记录给定日志记录所需的操作。这个版本应由子类实现,因此这里直接引发 NotImplementedError 异常。

格式器对象

Formatter 对象拥有以下的属性和方法。一般情况下,它们负责将 LogRecord 转换为可由人或外部系统解释的字符串。基础的 Formatter 允许指定格式字符串。如果未提供任何值,则使用默认值 '%(message)s' ,它仅将消息包括在日志记录调用中。要在格式化输出中包含其他信息(如时间戳),请阅读下文。

格式器可以使用格式化字符串来初始化,该字符串利用 LogRecord 的属性 —— 例如上述默认值,用户的消息和参数预先格式化为 LogRecordmessage 属性后被使用。此格式字符串包含标准的 Python %-s 样式映射键。

class logging.Formatter(fmt=None, datefmt=None, style=’%’, validate=True, **, defaults=None*)

返回 Formatter 类的新实例。实例将使用整个消息的格式字符串以及消息的日期/时间部分的格式字符串进行初始化。如果未指定 fmt ,则使用 '%(message)s'。如果未指定 datefmt,则使用 formatTime() 文档中描述的格式。

style 形参可以是 ‘%’, ‘{‘ 或 ‘$’ 之一,它决定格式字符串将如何与数据进行合并:使用 %-formatting, str.format() 或是 string.Template。 这仅适用于格式字符串 fmt (例如 '%(message)s'{message}),不适用于传递给 Logger.debug 的实际日志消息等;请参阅 生效于整个应用程序的格式化样式 了解有关在日志消息中使用 {- 和 $-formatting 的更多详情。

defaults 形参可以是一个包含在自定义字段中使用的默认值的字典。 例如: logging.Formatter('%(ip)s %(message)s', defaults={"ip": None})

在 3.2 版更改: 加入了 style 形参。

在 3.8 版更改: 加入validate 参数。不正确或不匹配的样式和格式将引发 ValueError 错误。例如: logging.Formatter('%(asctime)s - %(message)s', style='{')

在 3.10 版更改: 增加了 defaults 形参。

  • format(record)

    记录的属性字典被用作字符串格式化操作的操作数。 返回结果字符串。 在格式化该字典之前,会执行几个预备步骤。 记录的 message 属性是用 msg % args 来计算的。 如果格式化字符串包含 '(asctime)',则会调用 formatTime() 来格式化事件时间。 如果有异常信息,则使用 formatException() 将其格式化并添加到消息中。 请注意已格式化的异常信息会缓存在 exc_text 属性中。 这很有用因为异常信息可以被 pickle 并通过网络发送,但是如果你有不止一个对异常信息进行定制的 Formatter 子类则应当小心。 在这种情况下,你必须在一个格式化器完成格式化后清空缓存的值 (通过将 exc_text 属性设为 None),以便下一个处理事件的格式化器不会使用缓存的值,而是重新计算它。

    如果栈信息可用,它将被添加在异常信息之后,如有必要请使用 formatStack() 来转换它。

  • formatTime(record, datefmt=None)

    此方法应由想要使用格式化时间的格式器中的 format() 调用。可以在格式器中重写此方法以提供任何特定要求,但是基本行为如下:如果指定了 datefmt (字符串),则将其用于 time.strftime() 来格式化记录的创建时间。否则,使用格式 ‘%Y-%m-%d %H:%M:%S,uuu’,其中 uuu 部分是毫秒值,其他字母根据 time.strftime() 文档。这种时间格式的示例为 2003-01-23 00:29:50,411。返回结果字符串。

    此函数使用一个用户可配置函数将创建时间转换为元组。 默认情况下,使用 time.localtime();要为特定格式化程序实例更改此项,请将实例的 converter 属性设为具有与 time.localtime()time.gmtime() 相同签名的函数。 要为所有格式化程序更改此项,例如当你希望所有日志时间都显示为 GMT,请在 Formatter 类中设置 converter 属性。

    在 3.3 版更改: 在之前版本中,默认格式是被硬编码的,例如这个例子: 2010-09-06 22:38:15,292 其中逗号之前的部分由 strptime 格式字符串 ('%Y-%m-%d %H:%M:%S') 处理,而逗号之后的部分为毫秒值。 因为 strptime 没有表示毫秒的占位符,毫秒值使用了另外的格式字符串来添加 '%s,%03d' —- 这两个格式字符串代码都是硬编码在该方法中的。 经过修改,这些字符串被定义为类层级的属性,当需要时可以在实例层级上被重载。 属性的名称为 default_time_format (用于 strptime 格式字符串) 和 default_msec_format (用于添加毫秒值)。

    在 3.9 版更改: default_msec_format 可以为 None

  • formatException(exc_info)

    将指定的异常信息(由 sys.exc_info() 返回的标准异常元组)格式化为字符串。默认实现只是使用了 traceback.print_exception()。 结果字符串将被返回。

  • formatStack(stack_info)

    将指定的堆栈信息(由 traceback.print_stack() 返回的字符串,但移除末尾的换行符)格式化为字符串。 默认实现只是返回输入值。

过滤器对象

Filters 可被 HandlersLoggers 用来实现比按层级提供更复杂的过滤操作。 基本过滤器类只允许低于日志记录器层级结构中低于特定层级的事件。 例如,一个用 ‘A.B’ 初始化的过滤器将允许 ‘A.B’, ‘A.B.C’, ‘A.B.C.D’, ‘A.B.D’ 等日志记录器所记录的事件。 但 ‘A.BB’, ‘B.A.B’ 等则不允许。 如果用空字符串初始化,则所有事件都会通过。

class logging.Filter(name=’’)

返回一个 Filter 类的实例。 如果指定了 name*,则它将被用来为日志记录器命名,该类及其子类将通过该过滤器允许指定事件通过。 如果 *name 为空字符串,则允许所有事件通过。

  • filter(record)

    是否要记录指定的记录?返回零表示否,非零表示是。如果认为合适,则可以通过此方法就地修改记录。

请注意关联到处理器的过滤器会在事件由处理器发出之前被查询,而关联到日志记录器的过滤器则会在有事件被记录的的任何时候(使用 debug(), info() 等等)在将事件发送给处理器之前被查询。 这意味着由后代日志记录器生成的事件将不会被父代日志记录器的过滤器设置所过滤,除非该过滤器也已被应用于后代日志记录器。

你实际上不需要子类化 Filter :你可以传入任何一个包含有相同语义的 filter 方法的实例。

在 3.2 版更改: 你不需要创建专门的 Filter 类,或使用具有 filter 方法的其他类:你可以使用一个函数(或其他可调用对象)作为过滤器。 过滤逻辑将检查过滤器对象是否具有 filter 属性:如果有,就会将它当作是 Filter 并调用它的 filter() 方法。 在其他情况下,则会将它当作是可调用对象并将记录作为唯一的形参进行调用。 返回值应当与 filter() 的返回值相一致。

尽管过滤器主要被用来构造比层级更复杂的规则以过滤记录,但它们可以查看由它们关联的处理器或记录器所处理的每条记录:当你想要执行统计特定记录器或处理器共处理了多少条记录,或是在所处理的 LogRecord 中添加、修改或移除属性这样的任务时该特性将很有用处。 显然改变 LogRecord 时需要相当小心,但将上下文信息注入日志确实是被允许的。

LogRecord

LogRecord 实例是每当有日志被记录时由 Logger 自动创建的,并且可通过 makeLogRecord() 手动创建(例如根据从网络接收的已封存事件创建)。

class logging.LogRecord(name, level, pathname, lineno, msg, args, exc_info, func=None, sinfo=None)

包含与被记录的事件相关的所有信息。

主要信息是在 msgargs 中传递的,它们使用 msg % args 组合到一起以创建记录的 message 字段。

  • 参数

    • name — 用于记录由此 LogRecord 所表示事件的记录器名称。 请注意此名称将始终为该值,即使它可能是由附加到不同(祖先)日志记录器的处理器所发出的。
    • level — 以数字表示的日志记录事件层级(如 DEBUG, INFO 等)。 请注意这会转换为 LogRecord 的 两个 属性: levelno 为数字值而 levelname 为对应的层级名称。
    • pathname — 进行日志记录调用的文件的完整路径名。
    • lineno — 记录调用所在源文件中的行号。
    • msg — 事件描述消息,可能为带有可变数据占位符的格式字符串。
    • args — 要合并到 msg 参数以获得事件描述的可变数据。
    • exc_info — 包含当前异常信息的异常元组,或者如果没有可用异常信息则为 None
    • func — 发起调用日志记录调用的函数或方法名称。
    • sinfo — 一个文本字符串,表示当前线程中从堆栈底部直到日志记录调用的堆栈信息。
  • getMessage()

    在将 LogRecord 实例与任何用户提供的参数合并之后,返回此实例的消息。 如果用户提供给日志记录调用的消息参数不是字符串,则会在其上调用 str() 以将它转换为字符串。 此方法允许将用户定义的类用作消息,类的 __str__ 方法可以返回要使用的实际格式字符串。

在 3.2 版更改: 通过提供用于创建记录的工厂方法已使得 LogRecord 的创建更易于配置。 该工厂方法可使用 getLogRecordFactory()setLogRecordFactory() (在此可查看工厂方法的签名)来设置。

在创建时可使用此功能将你自己的值注入 LogRecord。 你可以使用以下模式:

old_factory = logging.getLogRecordFactory()
def record_factory(*args, **kwargs):
    record = old_factory(*args, **kwargs)
    record.custom_attribute = 0xdecafbad
    return record
logging.setLogRecordFactory(record_factory)

通过此模式,多个工厂方法可以被链接起来,并且只要它们不重载彼此的属性或是在无意中覆盖了上面列出的标准属性,就不会发生意外。

LogRecord 属性

LogRecord 具有许多属性,它们大多数来自于传递给构造器的形参。 (请注意 LogRecord 构造器形参与 LogRecord 属性的名称并不总是完全彼此对应的。) 这些属性可被用于将来自记录的数据合并到格式字符串中。 下面的表格(按字母顺序)列出了属性名称、它们的含义以及相应的 %-style 格式字符串内占位符。

如果是使用 {}-格式化(str.format()),你可以将 {attrname} 用作格式字符串内的占位符。 如果是使用 $-格式化(string.Template),则会使用 ${attrname} 的形式。 当然在这两种情况下,都应当将 attrname 替换为你想要使用的实际属性名称。

在 {}-格式化的情况下,你可以在属性名称之后放置指定的格式化旗标,并用冒号来分隔两者。 例如,占位符 {msecs:03d} 会将毫秒值 4 格式化为 004。 请参看 str.format() 文档了解你所能使用的选项的完整细节。

属性名称 格式 描述
args 此属性不需要用户进行格式化。 合并到 msg 以产生 message 的包含参数的元组,或是其中的值将被用于合并的字典(当只有一个参数且其类型为字典时)。
asctime %(asctime)s 表示 LogRecord 何时被创建的供人查看时间值。 默认形式为 ‘2003-07-08 16:49:45,896’ (逗号之后的数字为时间的毫秒部分)。
created %(created)f LogRecord 被创建的时间(即 time.time() 的返回值)。
exc_info 此属性不需要用户进行格式化。 异常元组(例如 sys.exc_info)或者如未发生异常则为 None
文件名 %(filename)s pathname 的文件名部分。
funcName %(funcName)s 函数名包括调用日志记录.
levelname %(levelname)s 消息文本记录级别(‘DEBUG’‘INFO’‘WARNING’‘ERROR’‘CRITICAL’)。
levelno %(levelno)s 消息数字的记录级别 (DEBUG, INFO, WARNING, ERROR, CRITICAL).
lineno %(lineno)d 发出日志记录调用所在的源行号(如果可用)。
message %(message)s 记入日志的消息,即 msg % args 的结果。 这是在发起调用 Formatter.format() 时设置的。
module — 模块 %(module)s 模块 (filename 的名称部分)。
msecs %(msecs)d LogRecord 被创建的时间的毫秒部分。
msg 此属性不需要用户进行格式化。 在原始日志记录调用中传入的格式字符串。 与 args 合并以产生 message,或是一个任意对象。
名称 %(name)s 用于记录调用的日志记录器名称。
pathname %(pathname)s 发出日志记录调用的源文件的完整路径名(如果可用)。
process %(process)d 进程ID(如果可用)
processName %(processName)s 进程名(如果可用)
relativeCreated %(relativeCreated)d 以毫秒数表示的 LogRecord 被创建的时间,即相对于 logging 模块被加载时间的差值。
stack_info 此属性不需要用户进行格式化。 当前线程中从堆栈底部起向上直到包括日志记录调用并引发创建当前记录堆栈帧创建的堆栈帧信息(如果可用)。
thread %(thread)d 线程ID(如果可用)
threadName %(threadName)s 线程名(如果可用)

在 3.1 版更改: 添加了 processName

LoggerAdapter 对象

LoggerAdapter 实例会被用来方便地将上下文信息传入日志记录调用。

class logging.LoggerAdapter(logger, extra)

返回一个 LoggerAdapter 的实例,该实例的初始化使用了下层的 Logger 实例和一个字典类对象。

  • process(msg, kwargs)

    修改传递给日志记录调用的消息和/或关键字参数以便插入上下文信息。 此实现接受以 extra 形式传给构造器的对象并使用 ‘extra’ 键名将其加入 kwargs。 返回值为一个 (msg, kwargs) 元组,其包含(可能经过修改的)传入参数。

在上述方法之外,LoggerAdapter 还支持 Logger 的下列方法: debug(), info()warning()error(), exception(), critical()log()isEnabledFor()getEffectiveLevel()setLevel() 以及 hasHandlers()。 这些方法具有与它们在 Logger 中的对应方法相同的签名,因此你可以互换使用这两种类型的实例。

在 3.2 版更改: isEnabledFor(), getEffectiveLevel(), setLevel()hasHandlers() 方法已被添加到 LoggerAdapter。 这些方法会委托给下层的日志记录器。

在 3.6 版更改: 增加了 manager 属性和 _log() 方法,它们会委托给下层的日志记录器并允许适配器嵌套。

线程安全

logging 模块的目标是使客户端不必执行任何特殊操作即可确保线程安全。 它通过使用线程锁来达成这个目标;用一个锁来序列化对模块共享数据的访问,并且每个处理程序也会创建一个锁来序列化对其下层 I/O 的访问。

如果你要使用 signal 模块来实现异步信号处理程序,则可能无法在这些处理程序中使用 logging。 这是因为 threading 模块中的锁实现并非总是可重入的,所以无法从此类信号处理程序发起调用。

模块级函数

在上述的类之外,还有一些模块级的函数。

logging.getLogger(name=None)

返回具有指定 name 的日志记录器,或者当 name 为 None 时返回层级结构中的根日志记录器。 如果指定了 name,它通常是以点号分隔的带层级结构的名称,如 ‘a’‘a.b’‘a.b.c.d’。 这些名称的选择完全取决于使用 logging 的开发者。

所有用给定的 name 对该函数的调用都将返回相同的日志记录器实例。 这意味着日志记录器实例不需要在应用的各部分间传递。

logging.getLoggerClass()

返回标准的 Logger 类,或是最近传给 setLoggerClass() 的类。 此函数可以从一个新的类定义中调用,以确保安装自定义的 Logger 类不会撤销其他代码已经应用的自定义操作。 例如:

class MyLogger(logging.getLoggerClass()):
    # ... override behaviour here

logging.getLogRecordFactory()

返回一个被用来创建 LogRecord 的可调用对象。

3.2 新版功能: 此函数与 setLogRecordFactory() 一起提供,以允许开发者对表示日志记录事件的 LogRecord 的构造有更好的控制。

请参阅 setLogRecordFactory() 了解有关如何调用该工厂方法的更多信息。

logging.debug(msg, \args, *kwargs)

在根日志记录器上记录一条 DEBUG 级别的消息。 msg 是消息格式字符串,而 args 是要使用字符串格式化运算符合并到 msg 的参数。 (请注意这意味着你可以在格式字符串中使用关键字以及单个字典参数。)

kwargs 中有三个关键字参数会被检查: exc_info 参数如果不为假值则会将异常信息添加到日志记录消息。 如果提供了异常元组(为 sys.exc_info() 的返回值格式)或异常实例则它会被使用;在其他情况下,会调用 sys.exc_info() 以获取异常信息。

第二个可选关键字参数是 stack_info*,默认为 False。如果为 True,则将堆栈信息添加到日志消息中,包括实际的日志调用。请注意,这与通过指定 *exc_info 显示的堆栈信息不同:前者是从堆栈底部到当前线程中的日志记录调用的堆栈帧,而后者是在搜索异常处理程序时,跟踪异常而打开的堆栈帧的信息。

您可以独立于 exc_info 来指定 stack_info,例如,即使在未引发任何异常的情况下,也可以显示如何到达代码中的特定点。堆栈帧在标题行之后打印:

Stack (most recent call last):

这模仿了显示异常帧时所使用的 Traceback (most recent call last):

第三个可选关键字参数是 extra,它可被用来传递一个字典,该字典会被用来填充为日志记录事件创建并附带用户自定义属性的 LogRecord 的 dict。 之后将可按你的需要使用这些自定义属性。 例如,可以将它们合并到已记录的消息中。 举例来说:

FORMAT = '%(asctime)-15s %(clientip)s %(user)-8s %(message)s'
logging.basicConfig(format=FORMAT)
d = {'clientip': '192.168.0.1', 'user': 'fbloggs'}
logging.warning('Protocol problem: %s', 'connection reset', extra=d)

应当会打印出这样的内容:

2006-02-08 22:20:02,165 192.168.0.1 fbloggs  Protocol problem: connection reset

extra 中传入的字典的键不应与日志系统使用的键冲突。

如果你选择在已记录的消息中使用这些属性,则需要格外小心。 例如在上面的示例中,Formatter 已设置了格式字符串,其在 LogRecord 的属性字典中应有 ‘clientip’ 和 ‘user’。 如果缺少这些属性,消息将不被记录,因为会引发字符串格式化异常,你始终需要传入带有这些键的 extra 字典。

尽管这可能很烦人,但此功能旨在用于特殊情况,例如在多个上下文中执行相同代码的多线程服务器,并且出现的有趣条件取决于此上下文(例如在上面的示例中就是远程客户端IP地址和已验证用户名)。在这种情况下,很可能将专门的 Formatter 与特定的 Handler 一起使用。

在 3.2 版更改: 增加了 stack_info 参数。

logging.info(msg, \args, *kwargs)

在根日志记录器上记录一条 INFO 级别的消息。

logging.warning(msg, \args, *kwargs)

在根日志记录器上记录一条 WARNING 级别的消息。

注解

有一个已过时方法 warn 其功能与 warning 一致。 由于 warn 已被弃用,请不要使用它 —— 而是改用 warning

logging.error(msg, \args, *kwargs)

在根日志记录器上记录一条 ERROR 级别的消息。

logging.critical(msg, \args, *kwargs)

在根日志记录器上记录一条 CRITICAL 级别的消息。

logging.exception(msg, \args, *kwargs)

在根日志记录器上记录一条 ERROR 级别的消息。 异常信息将被添加到日志消息中。 此函数应当仅从异常处理程序中调用。

logging.log(level, msg, \args, *kwargs)

在根日志记录器上记录一条 level 级别的消息。

注解

上述模块级的便捷函数均委托给根日志记录器,它们会调用 basicConfig() 以确保至少有一个处理器可用。 因此,在 Python 2.7.1 和 3.2 之前的版本中,除非线程启动 之前 已向根日志记录器添加了至少一个处理器,它们 不应 在线程中使用。 在较早的 Python 版本中,由于 basicConfig() 中存在线程安全性的不足,这(在少数情况下)可能导致处理器被多次加入根日志记录器,这会导致同一事件出现多条消息。

logging.disable(level=CRITICAL)

为所有日志记录器提供重载的级别 level*,其优先级高于日志记录器自己的级别。 当需要临时限制整个应用程序中的日志记录输出时,此功能会很有用。 它的效果是禁用所有重要程度为 *level 及以下的日志记录调用,因此如果你附带 INFO 值调用它,则所有 INFO 和 DEBUG 事件就会被丢弃,而重要程度为 WARNING 以及上的事件将根据日志记录器的当前有效级别来处理。 如果 logging.disable(logging.NOTSET) 被调用,它将移除这个重载的级别,因此日志记录输出会再次取决于单个日志记录器的有效级别。

请注意如果你定义了任何高于 CRITICAL 的自定义日志级别(并不建议这样做),你就将无法沿用 level 形参的默认值,而必须显式地提供适当的值。

在 3.7 版更改: level 形参默认级别为 CRITICAL。 请参阅 bpo-28524 了解此项改变的更多细节。

logging.addLevelName(level, levelName)

在一个内部字典中关联级别 level 与文本 levelName,该字典会被用来将数字级别映射为文本表示形式,例如在 Formatter 格式化消息的时候。 此函数也可被用来定义你自己的级别。 唯一的限制是自定义的所有级别必须使用此函数来注册,级别值必须为正整数并且其应随严重程度而递增。

logging.getLevelName(level)

返回日志记录级别 level 的字符串表示。

如果 level 为预定义的级别 CRITICAL, ERROR, WARNING, INFODEBUG 之一则你会得到相应的字符串。 如果你使用 addLevelName() 将级别关联到名称则返回你为 level 所关联的名称。 如果传入了与已定义级别相对应的数字值,则返回对应的字符串表示。

level 形参也接受级别的字符串表示例如 ‘INFO’。 在这种情况下,此函数将返回级别所对应的数字值。

如果未传入可匹配的数字或字符串值,则返回字符串 ‘Level %s’ % level。

注解

级别在内部以整数表示(因为它们在日志记录逻辑中需要进行比较)。 此函数被用于在整数级别与通过 %(levelname)s 格式描述符方式在格式化日志输出中显示的级别名称之间进行相互的转换 。

在 3.4 版更改: 在早于 3.4 的 Python 版本中,此函数也可传入一个字符串形式的级别名称,并将返回对应的级别数字值。 此未记入文档的行为被视为是一个错误,并在 Python 3.4 中被移除,但又在 3.4.2 中被恢复以保持向下兼容性。

logging.makeLogRecord(attrdict)

创建并返回一个新的 LogRecord 实例,实例属性由 attrdict 定义。 此函数适用于接受一个通过套接字传输的封存好的 LogRecord 属性字典,并在接收端将其重建为一个 LogRecord 实例。

logging.basicConfig(**kwargs)

通过使用默认的 Formatter 创建一个 StreamHandler 并将其加入根日志记录器来为日志记录系统执行基本配置。 如果没有为根日志记录器定义处理器则 debug(), info(), warning(), error()critical() 等函数将自动调用 basicConfig()

如果根日志记录器已配置了处理器则此函数将不执行任何操作,除非关键字参数 force 被设为 True

注解

此函数应当在其他线程启动之前从主线程被调用。 在 2.7.1 和 3.2 之前的 Python 版本中,如果此函数从多个线程被调用,一个处理器(在极少的情况下)有可能被多次加入根日志记录器,导致非预期的结果例如日志中的消息出现重复。

支持以下关键字参数。

格式 描述
filename 使用指定的文件名创建一个 FileHandler,而不是 StreamHandler
filemode 如果指定了 filename,则用此 模式 打开该文件。 默认模式为 ‘a’
format 使用指定的格式字符串作为处理器。 默认为属性以冒号分隔的 levelname, namemessage
datefmt 使用指定的日期/时间格式,与 time.strftime() 所接受的格式相同。
style 如果指定了 format,将为格式字符串使用此风格。 ‘%’, ‘{‘‘$’ 分别对应于 printf 风格, str.format()string.Template。 默认为 ‘%’
level 设置根记录器级别去指定 level.
stream 使用指定的流初始化 StreamHandler。 请注意此参数与 filename 不兼容 —— 如果两者同时存在,则会引发 ValueError
handlers 如果指定,这应为一个包含要加入根日志记录器的已创建处理器的可迭代对象。 任何尚未设置格式描述符的处理器将被设置为在此函数中创建的默认格式描述符。 请注意此参数与 filenamestream 不兼容 —— 如果两者同时存在,则会引发 ValueError
force 如果将此关键字参数指定为 true,则在执行其他参数指定的配置之前,将移除并关闭附加到根记录器的所有现有处理器。
encoding 如果此关键字参数与 filename 一同被指定,则其值会在创建 FileHandler 时被使用,因而也会在打开输出文件时被使用。
errors 如果此关键字参数与 filename 一同被指定,则其值会在创建 FileHandler 时被使用,因而也会在打开输出文件时被使用。 如果未指定,则会使用值 ‘backslashreplace’。 请注意如果指定为 None,它将被原样传给 open(),这意味着它将会当作传入 ‘errors’ 一样处理。

在 3.2 版更改: 增加了 style 参数。

在 3.3 版更改: 增加了 handlers 参数。 增加了额外的检查来捕获指定不兼容参数的情况 (例如同时指定 handlersstreamfilename*,或者同时指定 *streamfilename)。

在 3.8 版更改: 增加了 force 参数。

在 3.9 版更改: 增加了 encodingerrors 参数。

logging.shutdown()

通过刷新和关闭所有处理程序来通知日志记录系统执行有序停止。 此函数应当在应用退出时被调用并且在此调用之后不应再使用日志记录系统。

当 logging 模块被导入时,它会将此函数注册为退出处理程序,因此通常不需要手动执行该操作。

logging.setLoggerClass(klass)

通知日志记录系统在实例化日志记录器时使用 klass 类。 该类应当定义 __init__() 使其只要求一个 name 参数,并且 __init__() 应当调用 Logger.__init__()。 此函数通常会在需要使用自定义日志记录器行为的应用程序实例化任何日志记录器之前被调用。 在此调用之后,在任何其他时刻都不要使用该子类来直接实例化日志记录器:请继续使用 logging.getLogger() API 来获取你的日志记录器。

logging.setLogRecordFactory(factory)

设置一个用来创建 LogRecord 的可调用对象。

  • 参数

    factory — 用来实例化日志记录的工厂可调用对象。

3.2 新版功能: 此函数与 getLogRecordFactory() 一起提供,以便允许开发者对如何构造表示日志记录事件的 LogRecord 有更好的控制。

可调用对象 factory 具有如下签名:

factory(name, level, fn, lno, msg, args, exc_info, func=None, sinfo=None, **kwargs)
  • 名称

    日志记录器名称

    level

    日志记录级别(数字)。

    fn

    进行日志记录调用的文件的完整路径名。

    lno

    记录调用所在文件中的行号。

    msg

    日志消息。

    args

    日志记录消息的参数。

    exc_info

    异常元组,或 None

    func

    调用日志记录调用的函数或方法的名称。

    sinfo

    traceback.print_stack() 所提供的类似的栈回溯信息,显示调用的层级结构。

    kwargs

    其他关键字参数。

模块级属性

logging.lastResort

通过此属性提供的“最后处理者”。 这是一个以 WARNING 级别写入到 sys.stderrStreamHandler,用于在没有任何日志记录配置的情况下处理日志记录事件。 最终结果就是将消息打印到 sys.stderr,这会替代先前形式为 “no handlers could be found for logger XYZ” 的错误消息。 如果出于某种原因你需要先前的行为,可将 lastResort 设为 None

3.2 新版功能.

与警告模块集成

captureWarnings() 函数可用来将 loggingwarnings 模块集成。

logging.captureWarnings(capture)

此函数用于打开和关闭日志系统对警告的捕获。

如果 captureTrue,则 warnings 模块发出的警告将重定向到日志记录系统。具体来说,将使用 warnings.formatwarning() 格式化警告信息,并将结果字符串使用 WARNING 等级记录到名为 'py.warnings' 的记录器中。

如果 captureFalse,则将停止将警告重定向到日志记录系统,并且将警告重定向到其原始目标(即在 captureWarnings(True) 调用之前的有效目标)。

logging.config —- 日志记录配置

源代码: Lib/logging/config.py

配置函数

下列函数可配置 logging 模块。 它们位于 logging.config 模块中。 它们的使用是可选的 —- 要配置 logging 模块你可以使用这些函数,也可以通过调用主 API (在 logging 本身定义) 并定义在 logginglogging.handlers 中声明的处理程序。

logging.config.dictConfig(config)

从一个字典获取日志记录配置。

如果在配置期间遇到错误,此函数将引发 ValueError, TypeError, AttributeErrorImportError 并附带适当的描述性消息。 下面是将会引发错误的(可能不完整的)条件列表:

  • level 不是字符串或者不是对应于实际日志记录级别的字符串。
  • propagate 值不是布尔类型。
  • id 没有对应的目标。
  • 在增量调用期间发现不存在的处理程序 id。
  • 无效的日志记录器名称。
  • 无法解析为内部或外部对象。

解析由 DictConfigurator 类执行,该类的构造器可传入用于配置的字典,并且具有 configure() 方法。 logging.config 模块具有可调用属性 dictConfigClass,其初始值设为 DictConfigurator。 你可以使用你自己的适当实现来替换 dictConfigClass 的值。

dictConfig() 会调用 dictConfigClass 并传入指定的字典,然后在所返回的对象上调用 configure() 方法以使配置生效:

def dictConfig(config):
    dictConfigClass(config).configure()

例如,DictConfigurator 的子类可以在它自己的 __init__() 中调用 DictConfigurator.__init__(),然后设置可以在后续 configure() 调用中使用的自定义前缀。 dictConfigClass 将被绑定到这个新的子类,然后就可以与在默认的未定制状态下完全相同的方式调用 dictConfig()

3.2 新版功能.

logging.config.fileConfig(fname, defaults=None, disable_existing_loggers=True, encoding=None)

从一个 configparser 格式文件中读取日志记录配置。 文件格式应当与 配置文件格式 中的描述一致。 此函数可在应用程序中被多次调用,以允许最终用户在多个预设配置中进行选择(如果开发者提供了展示选项并加载选定配置的机制)。

  • 参数
    • fname — 一个文件名,或一个文件类对象,或是一个派生自 RawConfigParser 的实例。 如果传入了一个派生自 RawConfigParser 的实例,它会被原样使用。 否则,将会实例化一个 Configparser,并且它会从作为 fname 传入的对象中读取配置。 如果存在 readline() 方法,则它会被当作一个文件类对象并使用 read_file() 来读取;在其它情况下,它会被当作一个文件名并传递给 read()
    • defaults — 要传递给 ConfigParser 的默认值可在此参数中指定。
    • disable_existing_loggers — 如果指定为 False,则当执行此调用时已存在的日志记录器会被保持启用。 默认值为 True 因为这将以向下兼容的方式启用旧有行为。 此行为是禁用任何已存在的非根日志记录器除非它们或它们的上级在日志配置中被显式地指定。 :param encoding: 当 fname 为文件名时用于打开文件的编码格式。

在 3.4 版更改: 现在接受 RawConfigParser 子类的实例作为 fname 的值。 这有助于:

  • 使用一个配置文件,其中日志记录配置只是全部应用程序配置的一部分。
  • 使用从一个文件读取的配置,它随后会在被传给 fileConfig 之前由使用配置的应用程序来修改(例如基于命令行参数或运行时环境的其他部分)。

3.10 新版功能: 增加了 encoding 形参。

logging.config.listen(port=DEFAULT_LOGGING_CONFIG_PORT, verify=None)

在指定的端口上启动套接字服务器,并监听新的配置。 如果未指定端口,则会使用模块默认的 DEFAULT_LOGGING_CONFIG_PORT。 日志记录配置将作为适合由 dictConfig()fileConfig() 进行处理的文件来发送。 返回一个 Thread 实例,你可以在该实例上调用 start() 来启动服务器,对该服务器你可以在适当的时候执行 join()。 要停止该服务器,请调用 stopListening()

如果指定 verify 参数,则它应当是一个可调用对象,该对象应当验证通过套接字接收的字节数据是否有效且应被处理。 这可以通过对通过套接字发送的内容进行加密和/或签名来完成,这样 verify 可调用对象就能执行签名验证和/或解密。 verify 可调用对象的调用会附带一个参数 —— 通过套接字接收的字节数据 —— 并应当返回要处理的字节数据,或者返回 None 来指明这些字节数据应当被丢弃。 返回的字节数据可以与传入的字节数据相同(例如在只执行验证的时候),或者也可以完全不同(例如在可能执行了解密的时候)。

要将配置发送到套接字,请读取配置文件并将其作为字节序列发送到套接字,字节序列要以使用 struct.pack('>L', n) 打包为二进制格式的四字节长度的字符串打头。

注解

Because portions of the configuration are passed through eval(), use of this function may open its users to a security risk. While the function only binds to a socket on localhost, and so does not accept connections from remote machines, there are scenarios where untrusted code could be run under the account of the process which calls listen(). Specifically, if the process calling listen() runs on a multi-user machine where users cannot trust each other, then a malicious user could arrange to run essentially arbitrary code in a victim user’s process, simply by connecting to the victim’s listen() socket and sending a configuration which runs whatever code the attacker wants to have executed in the victim’s process. This is especially easy to do if the default port is used, but not hard even if a different port is used. To avoid the risk of this happening, use the verify argument to listen() to prevent unrecognised configurations from being applied.

在 3.4 版更改: 添加了 verify 参数。

注解

如果你希望将配置发送给未禁用现有日志记录器的监听器,你将需要使用 JSON 格式的配置,该格式将使用 dictConfig() 进行配置。 此方法允许你在你发送的配置中将 disable_existing_loggers 指定为 False

logging.config.stopListening()

停止通过对 listen() 的调用所创建的监听服务器。 此函数的调用通常会先于在 listen() 的返回值上调用 join()

配置字典架构

描述日志记录配置需要列出要创建的不同对象及它们之间的连接;例如,你可以创建一个名为 ‘console’ 的处理程序,然后名为 ‘startup’ 的日志记录器将可以把它的消息发送给 ‘console’ 处理程序。 这些对象并不仅限于 logging 模块所提供的对象,因为你还可以编写你自己的格式化或处理程序类。 这些类的形参可能还需要包括 sys.stderr 这样的外部对象。 描述这些对象和连接的语法会在下面的 对象连接 中定义。

字典架构细节

传给 dictConfig() 的字典必须包含以下的键:

  • version - 应设为代表架构版本的整数值。 目前唯一有效的值是 1,使用此键可允许架构在继续演化的同时保持向下兼容性。

所有其他键都是可选项,但如存在它们将根据下面的描述来解读。 在下面提到 ‘configuring dict’ 的所有情况下,都将检查它的特殊键 '()' 以确定是否需要自定义实例化。 如果需要,则会使用下面 用户定义对象 所描述的机制来创建一个实例;否则,会使用上下文来确定要实例化的对象。

  • formatters - 对应的值将是一个字典,其中每个键是一个格式器 ID 而每个值则是一个描述如何配置相应 Formatter 实例的字典。

    在配置字典中搜索以下可选键,这些键对应于创建 Formatter 对象时传入的参数。:

    • format
    • datefmt
    • style
    • validate (从版本 >=3.8 起)

    可选的 class 键指定格式化器类的名称(形式为带点号的模块名和类名)。 实例化的参数与 Formatter 的相同,因此这个键对于实例化自定义的 Formatter 子类最为有用。 如果,替代类可能 会以扩展和精简格式呈现异常回溯信息。 如果你的格式化器需要不同的或额外的配置键,你应当使用 用户定义对象。

  • filters - 对应的值将是一个字典,其中每个键是一个过滤器 ID 而每个值则是一个描述如何配置相应 Filter 实例的字典。

    将在配置字典中搜索键 name (默认值为空字符串) 并且该键会被用于构造 logging.Filter 实例。

  • handlers - 对应的值将是一个字典,其中每个键是一个处理程序 ID 而每个值则是一个描述如何配置相应 Handler 实例的字典。

    将在配置字典中搜索下列键:

    • class (强制)。 这是处理程序类的完整限定名称。
    • level (可选)。 处理程序的级别。
    • formatter (可选)。 处理程序所对应格式化器的 ID。
    • filters (可选)。 由处理程序所对应过滤器的 ID 组成的列表。

    所有 其他 键会被作为关键字参数传递给处理程序类的构造器。 例如,给定如下配置:

    handlers:
      console:
        class : logging.StreamHandler
        formatter: brief
        level   : INFO
        filters: [allow_foo]
        stream  : ext://sys.stdout
      file:
        class : logging.handlers.RotatingFileHandler
        formatter: precise
        filename: logconfig.log
        maxBytes: 1024
        backupCount: 3

    ID 为 console 的处理程序会被实例化为 logging.StreamHandler,并使用 sys.stdout 作为下层流。 ID 为 file 的处理程序会被实例化为 logging.handlers.RotatingFileHandler,并附带关键字参数 filename='logconfig.log', maxBytes=1024, backupCount=3

  • loggers - 对应的值将是一个字典,其中每个键是一个日志记录器名称而每个值则是一个描述如何配置相应 Logger 实例的字典。

    将在配置字典中搜索下列键:

    • level (可选)。 日志记录器的级别。
    • propagate (可选)。 日志记录器的传播设置。
    • filters (可选)。 由日志记录器对应过滤器的 ID 组成的列表。
    • handlers (可选)。 由日志记录器对应处理程序的 ID 组成的列表。

    指定的日志记录器将根据指定的级别、传播、过滤器和处理程序来配置。

  • root - 这将成为根日志记录器对应的配置。 配置的处理方式将与所有日志记录器一致,除了 propagate 设置将不可用之外。

  • incremental - 配置是否要被解读为在现有配置上新增。 该值默认为 False,这意味着指定的配置将以与当前 fileConfig() API 所使用的相同语义来替代现有的配置。

    如果指定的值为 True,配置会按照 增量配置 部分所描述的方式来处理。

  • disable_existing_loggers - 是否要禁用任何现有的非根日志记录器。 该设置对应于 fileConfig() 中的同名形参。 如果省略,则此形参默认为 True。 如果 incrementalTrue 则该省会被忽略。

增量配置

为增量配置提供完全的灵活性是很困难的。 例如,由于过滤器和格式化器这样的对象是匿名的,一旦完成配置,在增加配置时就不可能引用这些匿名对象。

此外,一旦完成了配置,在运行时任意改变日志记录器、处理程序、过滤器、格式化器的对象图就不是很有必要;日志记录器和处理程序的详细程度只需通过设置级别即可实现控制(对于日志记录器则可设置传播旗标)。 在多线程环境中以安全的方式任意改变对象图也许会导致问题;虽然并非不可能,但这样做的好处不足以抵销其所增加的实现复杂度。

这样,当配置字典的 incremental 键存在且为 True 时,系统将完全忽略任何 formattersfilters 条目,并仅会处理 handlers 条目中的 level 设置,以及 loggersroot 条目中的 levelpropagate 设置。

使用配置字典中的值可让配置以封存字典对象的形式通过线路传送给套接字监听器。 这样,长时间运行的应用程序的日志记录的详细程度可随时间改变而无须停止并重新启动应用程序。

对象连接

该架构描述了一组日志记录对象 —— 日志记录器、处理程序、格式化器、过滤器 —— 它们在对象图中彼此连接。 因此,该架构需要能表示对象之间的连接。 例如,在配置完成后,一个特定的日志记录器关联到了一个特定的处理程序。 出于讨论的目的,我们可以说该日志记录器代表两者间连接的源头,而处理程序则代表对应的目标。 当然在已配置对象中这是由包含对处理程序的引用的日志记录器来代表的。 在配置字典中,这是通过给每个目标对象一个 ID 来无歧义地标识它,然后在源头对象中使用该 ID 来实现的。

因此,举例来说,考虑以下 YAML 代码段:

formatters:
  brief:
    # configuration for formatter with id 'brief' goes here
  precise:
    # configuration for formatter with id 'precise' goes here
handlers:
  h1: #This is an id
   # configuration of handler with id 'h1' goes here
   formatter: brief
  h2: #This is another id
   # configuration of handler with id 'h2' goes here
   formatter: precise
loggers:
  foo.bar.baz:
    # other configuration for logger 'foo.bar.baz'
    handlers: [h1, h2]

(注:这里使用 YAML 是因为它的可读性比表示字典的等价 Python 源码形式更好。)

日志记录器 ID 就是日志记录器的名称,它会在程序中被用来获取对日志记录器的引用,例如 foo.bar.baz。 格式化器和过滤器的 ID 可以是任意字符串值 (例如上面的 brief, precise) 并且它们是瞬态的,因为它们仅对处理配置字典有意义并会被用来确定对象之间的连接,而当配置调用完成时不会在任何地方保留。

上面的代码片段指明名为 foo.bar.baz 的日志记录器应当关联到两个处理程序,它们的 ID 是 h1h2h1 的格式化器的 ID 是 brief,而 h2 的格式化器的 ID 是 precise

用户定义对象

此架构支持用户定义对象作为处理程序、过滤器和格式化器。 (日志记录器的不同实例不需要具有不同类型,因此这个配置架构并不支持用户定义日志记录器类。)

要配置的对象是由字典描述的,其中包含它们的配置详情。 在某些地方,日志记录系统将能够从上下文中推断出如何实例化一个对象,但是当要实例化一个用户自定义对象时,系统将不知道要如何做。 为了提供用户自定义对象实例化的完全灵活性,用户需要提供一个‘工厂’函数 —— 即在调用时传入配置字典并返回实例化对象的可调用对象。 这是用一个通过特殊键 '()' 来访问的工厂函数的绝对导入路径来标示的。 下面是一个实际的例子:

formatters:
  brief:
    format: '%(message)s'
  default:
    format: '%(asctime)s %(levelname)-8s %(name)-15s %(message)s'
    datefmt: '%Y-%m-%d %H:%M:%S'
  custom:
      (): my.package.customFormatterFactory
      bar: baz
      spam: 99.9
      answer: 42

上面的 YAML 代码片段定义了三个格式化器。 第一个的 ID 为 brief,是带有特殊格式字符串的标准 logging.Formatter 实例。 第二个的 ID 为 default,具有更长的格式同时还显式地定义了时间格式,并将最终实例化一个带有这两个格式字符串的 logging.Formatter。 以 Python 源代码形式显示的 briefdefault 格式化器分别具有下列配置子字典:

{
  'format' : '%(message)s'
}

和:

{
  'format' : '%(asctime)s %(levelname)-8s %(name)-15s %(message)s',
  'datefmt' : '%Y-%m-%d %H:%M:%S'
}

并且由于这些字典不包含特殊键 '()',实例化方式是从上下文中推断出来的:结果会创建标准的 logging.Formatter 实例。 第三个格式器的 ID 为 custom,对应配置子字典为:

{
  '()' : 'my.package.customFormatterFactory',
  'bar' : 'baz',
  'spam' : 99.9,
  'answer' : 42
}

并且它包含特殊键 '()',这意味着需要用户自定义实例化方式。 在此情况下,将使用指定的工厂可调用对象。 如果它本身就是一个可调用对象则将被直接使用 —— 否则如果你指定了一个字符串(如这个例子所示)则将使用正常的导入机制来定位实例的可调用对象。 调用该可调用对象将传入配置子字典中 剩余的 条目作为关键字参数。 在上面的例子中,调用将预期返回 ID 为 custom 的格式化器:

my.package.customFormatterFactory(bar='baz', spam=99.9, answer=42)

'()' 用作特殊键是因为它不是一个有效的关键字形参名称,这样就不会与调用中使用的关键字参数发生冲突。 '()' 还被用作表明对应值为可调用对象的助记符。

访问外部对象

有时一个配置需要引用配置以外的对象,例如 sys.stderr。 如果配置字典是使用 Python 代码构造的,这会很直观,但是当配置是通过文本文件(例如 JSON, YAML)提供的时候就会引发问题。 在一个文本文件中,没有将 sys.stderr 与字符串字面值 'sys.stderr' 区分开来的标准方式。 为了实现这种区分,配置系统会在字符串值中查找规定的特殊前缀并对其做特殊处理。 例如,如果在配置中将字符串字面值 'ext://sys.stderr' 作为一个值来提供,则 ext:// 将被去除而该值的剩余部分将使用正常导入机制来处理。

此类前缀的处理方式类似于协议处理:存在一种通用机制来查找与正则表达式 ^(?P<prefix>[a-z]+)://(?P<suffix>.*)$ 相匹配的前缀,如果识别出了 prefix,则 suffix 会以与前缀相对应的方式来处理并且处理的结果将替代原字符串值。 如果未识别出前缀,则原字符串将保持不变。

访问内部对象

除了外部对象,有时还需要引用配置中的对象。 这将由配置系统针对它所了解的内容隐式地完成。 例如,在日志记录器或处理程序中表示 level 的字符串值 'DEBUG' 将被自动转换为值 logging.DEBUG,而 handlers, filtersformatter 条目将接受一个对象 ID 并解析为适当的目标对象。

但是,对于 logging 模块所不了解的用户自定义对象则需要一种更通用的机制。 例如,考虑 logging.handlers.MemoryHandler,它接受一个 target 参数即其所委托的另一个处理程序。 由于系统已经知道存在该类,因而在配置中,给定的 target 只需为相应目标处理程序的的对象 ID 即可,而系统将根据该 ID 解析出处理程序。 但是,如果用户定义了一个具有 alternate 处理程序的 my.package.MyHandler,则配置程序将不知道 alternate 指向的是一个处理程序。 为了应对这种情况,通用解析系统允许用户指定:

handlers:
  file:
    # configuration of file handler goes here
  custom:
    (): my.package.MyHandler
    alternate: cfg://handlers.file

字符串字面值 'cfg://handlers.file' 将按照与 ext:// 前缀类似的方式被解析为结果字符串,但查找操作是在配置自身而不是在导入命名空间中进行。 该机制允许按点号或按索引来访问,与 str.format 所提供的方式类似。 这样,给定以下代码段:

handlers:
  email:
    class: logging.handlers.SMTPHandler
    mailhost: localhost
    fromaddr: my_app@domain.tld
    toaddrs:
      - support_team@domain.tld
      - dev_team@domain.tld
    subject: Houston, we have a problem.

在该配置中,字符串 'cfg://handlers' 将解析为带有 handlers 键的字典,字符串 'cfg://handlers.email 将解析为具有 email 键的 handlers 字典中的字典,依此类推。 字符串 'cfg://handlers.email.toaddrs[1] 将解析为 'dev_team@domain.tld' 而字符串 'cfg://handlers.email.toaddrs[0]' 将解析为值 'support_team@domain.tld'subject 值 可以使用 'cfg://handlers.email.subject' 或者等价的 'cfg://handlers.email[subject]' 来访问。 后一种形式仅在键包含空格或非字母类数字类字符的情况下才需要使用。 如果一个索引仅由十进制数码构成,则将尝试使用相应的整数值来访问,如果有必要则将回退为字符串值。

给定字符串 cfg://handlers.myhandler.mykey.123,这将解析为 config_dict['handlers']['myhandler']['mykey']['123']。 如果字符串被指定为 cfg://handlers.myhandler.mykey[123],系统将尝试从 config_dict['handlers']['myhandler']['mykey'][123] 中提取值,并在尝试失败时回退为 config_dict['handlers']['myhandler']['mykey']['123']

导入解析与定制导入器

导入解析默认使用内置的 __import__() 函数来执行导入。 你可能想要将其替换为你自己的导入机制:如果是这样的话,你可以替换 DictConfigurator 或其超类 BaseConfigurator 类的 importer 属性。 但是你必须小心谨慎,因为函数是从类中通过描述器方式来访问的。 如果你使用 Python 可调用对象来执行导入,并且你希望在类层级而不是在实例层级上定义它,则你需要用 staticmethod() 来装饰它。 例如:

from importlib import import_module
from logging.config import BaseConfigurator
BaseConfigurator.importer = staticmethod(import_module)

如果你是在一个配置器的 实例 上设置导入可调用对象则你不需要用 staticmethod() 来装饰。

配置文件格式

fileConfig() 所能理解的配置文件格式是基于 configparser 功能的。 该文件必须包含 [loggers], [handlers][formatters] 等小节,它们通过名称来标识文件中定义的每种类型的实体。 对于每个这样的实体,都有单独的小节来标识实体的配置方式。 因此,对于 [loggers] 小节中名为 log01 的日志记录器,相应的配置详情保存在 [logger_log01] 小节中。 类似地,对于 [handlers] 小节中名为 hand01 的处理程序,其配置将保存在名为 [handler_hand01] 的小节中,而对于 [formatters] 小节中名为 form01 的格式化器,其配置将在名为 [formatter_form01] 的小节中指定。 根日志记录器的配置必须在名为 [logger_root] 的小节中指定。

注解

fileConfig() API 比 dictConfig() API 更旧因而没有提供涵盖日志记录特定方面的功能。 例如,你无法配置 Filter 对象,该对象使用 fileConfig() 提供超出简单整数级别的消息过滤功能。 如果你想要在你的日志记录配置中包含 Filter 的实例,你将必须使用 dictConfig()。 请注意未来还将向 dictConfig() 添加对配置功能的强化,因此值得考虑在方便的时候转换到这个新 API。

在文件中这些小节的例子如下所示。

[loggers]
keys=root,log02,log03,log04,log05,log06,log07
[handlers]
keys=hand01,hand02,hand03,hand04,hand05,hand06,hand07,hand08,hand09
[formatters]
keys=form01,form02,form03,form04,form05,form06,form07,form08,form09

根日志记录器必须指定一个级别和一个处理程序列表。 根日志小节的例子如下所示。

[logger_root]
level=NOTSET
handlers=hand01

level 条目可以为 DEBUG, INFO, WARNING, ERROR, CRITICALNOTSET 之一。 其中 NOTSET 仅适用于根日志记录器,表示将会记录所有消息。 级别值会在 logging 包命名空间的上下文中通过 eval() 来得出。

handlers 条目是以逗号分隔的处理程序名称列表,它必须出现于 [handlers] 小节并且在配置文件中有相应的小节。

对于根日志记录器以外的日志记录器,还需要某些附加信息。 下面的例子演示了这些信息。

[logger_parser]
level=DEBUG
handlers=hand01
propagate=1
qualname=compiler.parser

levelhandlers 条目的解释方式与根日志记录器的一致,不同之处在于如果一个非根日志记录器的级别被指定为 NOTSET,则系统会咨询更高层级的日志记录器来确定该日志记录器的有效级别。 propagate 条目设为 1 表示消息必须从此日志记录器传播到更高层级的处理程序,设为 0 表示消息 不会 传播到更高层级的处理程序。 qualname 条目是日志记录器的层级通道名称,也就是应用程序获取日志记录器所用的名称。

指定处理程序配置的小节说明如下。

[handler_hand01]
class=StreamHandler
level=NOTSET
formatter=form01
args=(sys.stdout,)

class 条目指明处理程序的类(由 logging 包命名空间中的 eval() 来确定)。 level 会以与日志记录器相同的方式来解读,NOTSET 会被视为表示‘记录一切消息’。

formatter 条目指明此处理程序的格式化器的键名称。 如为空白,则会使用默认的格式化器 (logging._defaultFormatter)。 如果指定了名称,则它必须出现于 [formatters] 小节并且在配置文件中有相应的小节。

args 条目,当在 logging 包命名空间的上下文中执行 eval() 时将是传给处理程序类构造器的参数列表。 请参阅相应处理程序的构造器说明或者下面的示例,以了解典型的条目是如何构造的。 如果未提供,则默认为 ()

可选的 kwargs 条目,当在 logging 包命名空间的上下文中执行 eval() 时将是传给处理程序的构造器的关键字参数字典。 如果未提供,则默认为 {}

[handler_hand02]
class=FileHandler
level=DEBUG
formatter=form02
args=('python.log', 'w')
[handler_hand03]
class=handlers.SocketHandler
level=INFO
formatter=form03
args=('localhost', handlers.DEFAULT_TCP_LOGGING_PORT)
[handler_hand04]
class=handlers.DatagramHandler
level=WARN
formatter=form04
args=('localhost', handlers.DEFAULT_UDP_LOGGING_PORT)
[handler_hand05]
class=handlers.SysLogHandler
level=ERROR
formatter=form05
args=(('localhost', handlers.SYSLOG_UDP_PORT), handlers.SysLogHandler.LOG_USER)
[handler_hand06]
class=handlers.NTEventLogHandler
level=CRITICAL
formatter=form06
args=('Python Application', '', 'Application')
[handler_hand07]
class=handlers.SMTPHandler
level=WARN
formatter=form07
args=('localhost', 'from@abc', ['user1@abc', 'user2@xyz'], 'Logger Subject')
kwargs={'timeout': 10.0}
[handler_hand08]
class=handlers.MemoryHandler
level=NOTSET
formatter=form08
target=
args=(10, ERROR)
[handler_hand09]
class=handlers.HTTPHandler
level=NOTSET
formatter=form09
args=('localhost:9022', '/log', 'GET')
kwargs={'secure': True}

指定格式化器配置的小节说明如下。

[formatter_form01]
format=F1 %(asctime)s %(levelname)s %(message)s
datefmt=
style='%'
validate=True
class=logging.Formatter

用于格式化器配置的参数与字典规范 格式化器部分 中的键相同。

注解

由于如上所述使用了 eval(),因此使用 listen() 通过套接字来发送和接收配置会导致潜在的安全风险。 此风险仅限于相互间没有信任的多个用户在同一台机器上运行代码的情况.

logging.handlers —- 日志处理程序

源代码: Lib/logging/handlers.py

这个包提供了以下有用的处理程序。 请注意有三个处理程序类 (StreamHandler, FileHandlerNullHandler) 实际上是在 logging 模块本身定义的,但其文档与其他处理程序一同记录在此。

StreamHandler

StreamHandler 类位于核心 logging 包,它可将日志记录输出发送到数据流例如 sys.stdout, sys.stderr 或任何文件类对象(或者更精确地说,任何支持 write()flush() 方法的对象)。

class logging.StreamHandler(stream=None)

返回一个新的 StreamHandler 类。 如果指定了 stream,则实例将用它作为日志记录输出;在其他情况下将使用 sys.stderr

  • emit(record)

    如果指定了一个格式化器,它会被用来格式化记录。 随后记录会被写入到 terminator 之后的流中。 如果存在异常信息,则会使用 traceback.print_exception() 来格式化并添加到流中。

  • flush()

    通过调用流的 flush() 方法来刷新它。 请注意 close() 方法是继承自 Handler 的所以没有输出,因此有时可能需要显式地调用 flush()

  • setStream(stream)

    将实例的流设为指定值,如果两者不一致的话。 旧的流会在设置新的流之前被刷新。

    • 参数

      stream — 处理程序应当使用的流。

      返回

      旧的流,如果流已被改变的话,如果未被改变则为 None

    3.7 新版功能.

  • terminator

    当将已格式化的记录写入到流时被用作终止符的字符串。 默认值为 '\n'

    如果你不希望以换行符终止,你可以将处理程序类实例的 terminator 属性设为空字符串。

    在较早的版本中,终止符被硬编码为 '\n'

    3.2 新版功能.

FileHandler

FileHandler 类位于核心 logging 包,它可将日志记录输出到磁盘文件中。 它从 StreamHandler 继承了输出功能。

class logging.FileHandler(filename, mode=’a’, encoding=None, delay=False, errors=None)

返回一个 FileHandler 类的新实例。 将打开指定的文件并将其用作日志记录流。 如果未指定 mode*,则会使用 'a'。 如果 *encoding 不为 None,则会将其用作打开文件的编码格式。 如果 delay 为真值,则文件打开会被推迟至第一次调用 emit() 时。 默认情况下,文件会无限增长。 如果指定了 errors,它会被用于确定编码格式错误的处理方式。

在 3.6 版更改: 除了字符串值,也接受 Path 对象作为 filename 参数。

在 3.9 版更改: 增加了 errors 形参。

  • close()

    关闭文件。

  • emit(record)

    将记录输出到文件。

NullHandler

3.1 新版功能.

NullHandler 类位于核心 logging 包,它不执行任何格式化或输出。 它实际上是一个供库开发者使用的‘无操作’处理程序。

class logging.NullHandler

返回一个 NullHandler 类的新实例。

  • emit(record)

    此方法不执行任何操作。

  • handle(record)

    此方法不执行任何操作。

  • createLock()

    此方法会对锁返回 None,因为没有下层 I/O 的访问需要被序列化。

WatchedFileHandler

WatchedFileHandler 类位于 logging.handlers 模块,这个 FileHandler 用于监视它所写入日志记录的文件。 如果文件发生变化,它会被关闭并使用文件名重新打开。

发生文件更改可能是由于使用了执行文件轮换的程序例如 newsysloglogrotate。 这个处理程序在 Unix/Linux 下使用,它会监视文件来查看自上次发出数据后是否有更改。 (如果文件的设备或 inode 发生变化就认为已被更改。) 如果文件被更改,则会关闭旧文件流,并再打开文件以获得新文件流。

这个处理程序不适合在 Windows 下使用,因为在 Windows 下打开的日志文件无法被移动或改名 —— 日志记录会使用排他的锁来打开文件 —— 因此这样的处理程序是没有必要的。 此外,ST_INO 在 Windows 下不受支持;stat() 将总是为该值返回零。

class logging.handlers.WatchedFileHandler(filename, mode=’a’, encoding=None, delay=False, errors=None)

返回一个 WatchedFileHandler 类的新实例。 将打开指定的文件并将其用作日志记录流。 如果未指定 mode*,则会使用 'a'。 如果 *encoding 不为 None,则会将其用作打开文件的编码格式。 如果 delay 为真值,则文件打开会被推迟至第一次调用 emit() 时。 默认情况下,文件会无限增长。 如果提供了 errors,它会被用于确定编码格式错误的处理方式。

在 3.6 版更改: 除了字符串值,也接受 Path 对象作为 filename 参数。

在 3.9 版更改: 增加了 errors 形参。

  • reopenIfNeeded()

    检查文件是否已更改。 如果已更改,则会刷新并关闭现有流然后重新打开文件,这通常是将记录输出到文件的先导操作。

    3.6 新版功能.

  • emit(record)

    将记录输出到文件,但如果文件已更改则会先调用 reopenIfNeeded() 来重新打开它。

BaseRotatingHandler

BaseRotatingHandler 类位于 logging.handlers 模块中,它是轮换文件处理程序类 RotatingFileHandlerTimedRotatingFileHandler 的基类。 你不需要实例化此类,但它具有你可能需要重载的属性和方法。

class logging.handlers.BaseRotatingHandler(filename, mode, encoding=None, delay=False, errors=None)

类的形参与 FileHandler 的相同。 其属性有:

  • namer

    如果此属性被设为一个可调用对象,则 rotation_filename() 方法会委托给该可调用对象。 传给该可调用对象的形参与传给 rotation_filename() 的相同。

    注解

    namer 函数会在轮换期间被多次调用,因此它应当尽可能的简单快速。 它还应当对给定的输入每次都返回相同的输出,否则轮换行为可能无法按预期工作。

    3.3 新版功能.

  • rotator

    如果此属性被设为一个可调用对象,则 rotate() 方法会委托给该可调用对象。 传给该可调用对象的形参与传给 rotate() 的相同。

    3.3 新版功能.

  • rotation_filename(default_name)

    当轮换时修改日志文件的文件名。

    提供该属性以便可以提供自定义文件名。

    默认实现会调用处理程序的 ‘namer’ 属性,如果它是可调用对象的话,并传给它默认的名称。 如果该属性不是可调用对象 (默认值为 None),则将名称原样返回。

    • 参数

      default_name — 日志文件的默认名称。

    3.3 新版功能.

  • rotate(source, dest)

    当执行轮换时,轮换当前日志。

    默认实现会调用处理程序的 ‘rotator’ 属性,如果它是可调用对象的话,并传给它 source 和 dest 参数。 如果该属性不是可调用对象 (默认值为 None),则将源简单地重命名为目标。

    • 参数
      • source — 源文件名。 这通常为基本文件名,例如 ‘test.log’。
      • dest — 目标文件名。 这通常是源被轮换后的名称,例如 ‘test.log.1’。

3.3 新版功能.

该属性存在的理由是让你不必进行子类化 —— 你可以使用与 RotatingFileHandlerTimedRotatingFileHandler 的实例相同的可调用对象。 如果 namer 或 rotator 可调用对象引发了异常,将会按照与 emit() 调用期间的任何其他异常相同的方式来处理,例如通过处理程序的 handleError() 方法。

如果你需要对轮换进程执行更多的修改,你可以重载这些方法。

RotatingFileHandler

RotatingFileHandler 类位于 logging.handlers 模块,它支持磁盘日志文件的轮换。

class logging.handlers.RotatingFileHandler(filename, mode=’a’, maxBytes=0, backupCount=0, encoding=None, delay=False, errors=None)

返回一个 RotatingFileHandler 类的新实例。 将打开指定的文件并将其用作日志记录流。 如果未指定 mode*,则会使用 'a'。 如果 *encoding 不为 None,则会将其用作打开文件的编码格式。 如果 delay 为真值,则文件打开会被推迟至第一次调用 emit()。 默认情况下,文件会无限增长。 如果提供了 errors,它会被用于确定编码格式错误的处理方式。

你可以使用 maxBytesbackupCount 值来允许文件以预定的大小执行 rollover*。 当即将超出预定大小时,将关闭旧文件并打开一个新文件用于输出。 只要当前日志文件长度接近 *maxBytes 就会发生轮换;但是如果 maxBytesbackupCount 两者之一的值为零,就不会发生轮换,因此你通常要设置 backupCount 至少为 1,而 maxBytes 不能为零。 当 backupCount 为非零值时,系统将通过为原文件名添加扩展名 ‘.1’, ‘.2’ 等来保存旧日志文件。 例如,当 backupCount 为 5 而基本文件名为 app.log 时,你将得到 app.log, app.log.1, app.log.2 直至 app.log.5。 当前被写入的文件总是 app.log。 当此文件写满时,它会被关闭并重户名为 app.log.1,而如果文件 app.log.1, app.log.2 等存在,则它们会被分别重命名为 app.log.2, app.log.3 等等。

在 3.6 版更改: 除了字符串值,也接受 Path 对象作为 filename 参数。

在 3.9 版更改: 增加了 errors 形参。

  • doRollover()

    执行上文所描述的轮换。

  • emit(record)

    将记录输出到文件,以适应上文所描述的轮换。

TimedRotatingFileHandler

TimedRotatingFileHandler 类位于 logging.handlers 模块,它支持基于特定时间间隔的磁盘日志文件轮换。

class logging.handlers.TimedRotatingFileHandler(filename, when=’h’, interval=1, backupCount=0, encoding=None, delay=False, utc=False, atTime=None, errors=None)

返回一个新的 TimedRotatingFileHandler 类实例。 指定的文件会被打开并用作日志记录的流。 对于轮换操作它还会设置文件名前缀。 轮换的发生是基于 wheninterval 的积。

你可以使用 when 来指定 interval 的类型。 可能的值列表如下。 请注意它们不是大小写敏感的。

间隔类型 如果/如何使用 atTime
‘S’ 忽略
‘M’ 分钟 忽略
‘H’ 小时 忽略
‘D’ 忽略
‘W0’-‘W6’ 工作日(0=星期一) 用于计算初始轮换时间
‘midnight’ 如果未指定 atTime 则在午夜执行轮换,否则将使用 atTime 用于计算初始轮换时间

当使用基于星期的轮换时,星期一为 ‘W0’,星期二为 ‘W1’,以此类推直至星期日为 ‘W6’。 在这种情况下,传入的 interval 值不会被使用。

系统将通过为文件名添加扩展名来保存旧日志文件。 扩展名是基于日期和时间的,根据轮换间隔的长短使用 strftime 格式 %Y-%m-%d_%H-%M-%S 或是其中有变动的部分。

当首次计算下次轮换的时间时(即当处理程序被创建时),现有日志文件的上次被修改时间或者当前时间会被用来计算下次轮换的发生时间。

如果 utc 参数为真值,将使用 UTC 时间;否则会使用本地时间。

如果 backupCount 不为零,则最多将保留 backupCount 个文件,而如果当轮换发生时创建了更多的文件,则最旧的文件会被删除。 删除逻辑使用间隔时间来确定要删除的文件,因此改变间隔时间可能导致旧文件被继续保留。

如果 delay 为真值,则会将文件打开延迟到首次调用 emit() 的时候。

如果 atTime 不为 None,则它必须是一个 datetime.time 的实例,该实例指定轮换在一天内的发生时间,用于轮换被设为“在午夜”或“在每星期的某一天”之类的情况。 请注意在这些情况下,atTime 值实际上会被用于计算 初始 轮换,而后续轮换将会通过正常的间隔时间计算来得出。

如果指定了 errors,它会被用来确定编码错误的处理方式。

注解

初始轮换时间的计算是在处理程序被初始化时执行的。 后续轮换时间的计算则仅在轮换发生时执行,而只有当提交输出时轮换才会发生。 如果不记住这一点,你就可能会感到困惑。 例如,如果设置时间间隔为“每分钟”,那并不意味着你总会看到(文件名中)带有间隔一分钟时间的日志文件;如果在应用程序执行期间,日志记录输出的生成频率高于每分钟一次,那么 你可以预期看到间隔一分钟时间的日志文件。 另一方面,如果(假设)日志记录消息每五分钟才输出一次,那么文件时间将会存在对应于没有输出(因而没有轮换)的缺失。

在 3.4 版更改: 添加了 atTime 形参。

在 3.6 版更改: 除了字符串值,也接受 Path 对象作为 filename 参数。

在 3.9 版更改: 增加了 errors 形参。

  • doRollover()

    执行上文所描述的轮换。

  • emit(record)

    将记录输出到文件,以适应上文所描述的轮换。

SocketHandler

SocketHandler 类位于 logging.handlers 模块,它会将日志记录输出发送到网络套接字。 基类所使用的是 TCP 套接字。

class logging.handlers.SocketHandler(host, port)

返回一个 SocketHandler 类的新实例,该实例旨在与使用 hostport 给定地址的远程主机进行通信。

在 3.4 版更改: 如果 port 指定为 None,会使用 host 中的值来创建一个 Unix 域套接字 —— 在其他情况下,则会创建一个 TCP 套接字。

  • close()

    关闭套接字。

  • emit()

    对记录的属性字典执行封存并以二进制格式将其写入套接字。 如果套接字存在错误,则静默地丢弃数据包。 如果连接在此之前丢失,则重新建立连接。 要在接收端将记录解封并输出到 LogRecord,请使用 makeLogRecord() 函数。

  • handleError()

    处理在 emit() 期间发生的错误。 最可能的原因是连接丢失。 关闭套接字以便我们能在下次事件时重新尝试。

  • makeSocket()

    这是一个工厂方法,它允许子类定义它们想要的套接字的准确类型。 默认实现会创建一个 TCP 套接字 (socket.SOCK_STREAM)。

  • makePickle(record)

    将记录的属性字典封存为带有长度前缀的二进制格式,并将其返回以准备通过套接字进行传输。 此操作在细节上相当于:

    data = pickle.dumps(record_attr_dict, 1)datalen = struct.pack('>L', len(data))return datalen + data

    请注意封存操作不是绝对安全的。 如果你关心安全问题,你可能会想要重载此方法以实现更安全的机制。 例如,你可以使用 HMAC 对封存对象进行签名然后在接收端验证它们,或者你也可以在接收端禁用全局对象的解封操作。

  • send(packet)

    将封存后的字节串 packet 发送到套接字。 所发送字节串的格式与 makePickle() 文档中的描述一致。

    此函数允许部分发送,这可能会在网络繁忙时发生。

  • createSocket()

    尝试创建一个套接字;失败时将使用指数化回退算法处理。 在失败初次发生时,处理程序将丢弃它正尝试发送的消息。 当后续消息交由同一实例处理时,它将不会尝试连接直到经过一段时间以后。 默认形参设置为初始延迟一秒,如果在延迟之后连接仍然无法建立,处理程序将每次把延迟翻倍直至达到 30 秒的最大值。

    此行为由下列处理程序属性控制:

    • retryStart (初始延迟,默认为 1.0 秒)。
    • retryFactor (倍数,默认为 2.0)。
    • retryMax (最大延迟,默认为 30.0 秒)。

    这意味着如果远程监听器在处理程序被使用 之后 启动,你可能会丢失消息(因为处理程序在延迟结束之前甚至不会尝试连接,而在延迟期间静默地丢弃消息)。

DatagramHandler

DatagramHandler 类位于 logging.handlers 模块,它继承自 SocketHandler,支持通过 UDP 套接字发送日志记录消息。

class logging.handlers.DatagramHandler(host, port)

返回一个 DatagramHandler 类的新实例,该实例旨在与使用 hostport 给定地址的远程主机进行通信。

在 3.4 版更改: 如果 port 指定为 None,会使用 host 中的值来创建一个 Unix 域套接字 —— 在其他情况下,则会创建一个 UDP 套接字。

  • emit()

    对记录的属性字典执行封存并以二进制格式将其写入套接字。 如果套接字存在错误,则静默地丢弃数据包。 要在接收端将记录解封并输出到 LogRecord,请使用 makeLogRecord() 函数。

  • makeSocket()

    SocketHandler 的工厂方法会在此被重载以创建一个 UDP 套接字 (socket.SOCK_DGRAM)。

  • send(s)

    将封存后的字节串发送到套接字。 所发送字节串的格式与 SocketHandler.makePickle() 文档中的描述一致。

SysLogHandler

SysLogHandler 类位于 logging.handlers 模块,它支持将日志记录消息发送到远程或本地 Unix syslog。

class logging.handlers.SysLogHandler(address=’localhost’, SYSLOG_UDP_PORT, facility=LOG_USER, socktype=socket.SOCK_DGRAM)

返回一个 SysLogHandler 类的新实例用来与通过 address(host, port) 元组形式给出地址的远程 Unix 机器进行通讯。 如果未指定 address,则使用 ('localhost', 514)。 该地址会被用于打开套接字。 提供 (host, port) 元组的一种替代方式是提供字符串形式的地址,例如 ‘/dev/log’。 在这种情况下,会使用 Unix 域套接字将消息发送到 syslog。 如果未指定 *facility,则使用 LOG_USER。 打开的套接字类型取决于 *socktype 参数,该参数的默认值为 socket.SOCK_DGRAM 即打开一个 UDP 套接字。 要打开一个 TCP 套接字(用来配合较新的 syslog 守护程序例如 rsyslog 使用),请指定值为 socket.SOCK_STREAM

请注意如果你的服务器不是在 UDP 端口 514 上进行侦听,则 SysLogHandler 可能无法正常工作。 在这种情况下,请检查你应当为域套接字所使用的地址 —— 它依赖于具体的系统。 例如,在 Linux 上通常为 ‘/dev/log’ 而在 OS/X 上则为 ‘/var/run/syslog’。 你需要检查你的系统平台并使用适当的地址(如果你的应用程序需要在多个平台上运行则可能需要在运行时进行这样的检查)。 在 Windows 上,你大概必须要使用 UDP 选项。

在 3.2 版更改: 添加了 socktype

  • close()

    关闭连接远程主机的套接字。

  • emit(record)

    记录会被格式化,然后发送到 syslog 服务器。 如果存在异常信息,则它 不会 被发送到服务器。

    在 3.2.1 版更改: (参见: bpo-12168。) 在较早的版本中,发送至 syslog 守护程序的消息总是以一个 NUL 字节结束,因为守护程序的早期版本期望接收一个以 NUL 结束的消息 —— 即使它不包含于对应的规范说明 (RFC 5424)。 这些守护程序的较新版本不再期望接收 NUL 字节,如果其存在则会将其去除,而最新的守护程序(更紧密地遵循 RFC 5424)会将 NUL 字节作为消息的一部分传递出去。

    为了在面对所有这些不同守护程序行为时能够更方便地处理 syslog 消息,通过使用类层级属性 append_nul,添加 NUL 字节的操作已被作为可配置项。 该属性默认为 True (保留现有行为) 但可在 SysLogHandler 实例上设为 False 以便让实例 不会 添加 NUL 结束符。

    在 3.3 版更改: (参见: bpo-12419。) 在较早的版本中,没有 “ident” 或 “tag” 前缀工具可以用来标识消息的来源。 现在则可以使用一个类层级属性来设置它,该属性默认为 "" 表示保留现有行为,但可在 SysLogHandler 实例上重载以便让实例不会为所处理的每条消息添加标识。 请注意所提供的标识必须为文本而非字节串,并且它会被原封不动地添加到消息中。

  • encodePriority(facility, priority)

    将功能和优先级编码为一个整数。 你可以传入字符串或者整数 —— 如果传入的是字符串,则会使用内部的映射字典将其转换为整数。

    符号 LOG_ 的值在 SysLogHandler 中定义并且是 sys/syslog.h 头文件中所定义值的镜像。

    优先级

    名称(字符串) 符号值
    alert LOG_ALERT
    critcritical LOG_CRIT
    debug LOG_DEBUG
    emergpanic LOG_EMERG
    errerror LOG_ERR
    info LOG_INFO
    notice LOG_NOTICE
    warnwarning LOG_WARNING

    设备

    名称(字符串) 符号值
    auth LOG_AUTH
    authpriv LOG_AUTHPRIV
    cron LOG_CRON
    daemon LOG_DAEMON
    ftp LOG_FTP
    kern LOG_KERN
    lpr LOG_LPR
    mail LOG_MAIL
    news LOG_NEWS
    syslog LOG_SYSLOG
    user LOG_USER
    uucp LOG_UUCP
    local0 LOG_LOCAL0
    local1 LOG_LOCAL1
    local2 LOG_LOCAL2
    local3 LOG_LOCAL3
    local4 LOG_LOCAL4
    local5 LOG_LOCAL5
    local6 LOG_LOCAL6
    local7 LOG_LOCAL7
  • mapPriority(levelname)

    将日志记录级别名称映射到 syslog 优先级名称。 如果你使用自定义级别,或者如果默认算法不适合你的需要,你可能需要重载此方法。 默认算法将 DEBUG, INFO, WARNING, ERRORCRITICAL 映射到等价的 syslog 名称,并将所有其他级别名称映射到 ‘warning’。

NTEventLogHandler

NTEventLogHandler 类位于 logging.handlers 模块,它支持将日志记录消息发送到本地 Windows NT, Windows 2000 或 Windows XP 事件日志。 在你使用它之前,你需要安装 Mark Hammond 的 Python Win32 扩展。

class logging.handlers.NTEventLogHandler(appname, dllname=None, logtype=’Application’)

返回一个 NTEventLogHandler 类的新实例。 appname 用来定义出现在事件日志中的应用名称。 将使用此名称创建适当的注册表条目。 dllname 应当给出要包含在日志中的消息定义的 .dll 或 .exe 的完整限定路径名称(如未指定则会使用 'win32service.pyd' —— 此文件随 Win32 扩展安装且包含一些基本的消息定义占位符。 请注意使用这些占位符将使你的事件日志变得很大,因为整个消息源都会被放入日志。 如果你希望有较小的日志,你必须自行传入包含你想要在事件日志中使用的消息定义的 .dll 或 .exe 名称)。 logtype'Application', 'System''Security' 之一,且默认值为 'Application'

  • close()

    这时,你就可以从注册表中移除作为事件日志条目来源的应用名称。 但是,如果你这样做,你将无法如你所预期的那样在事件日志查看器中看到这些事件 —— 它必须能访问注册表来获取 .dll 名称。 当前版本并不会这样做。

  • emit(record)

    确定消息 ID,事件类别和事件类型,然后将消息记录到 NT 事件日志中。

  • getEventCategory(record)

    返回记录的事件类别。 如果你希望指定你自己的类别就要重载此方法。 此版本将返回 0。

  • getEventType(record)

    返回记录的事件类型。 如果你希望指定你自己的类型就要重载此方法。 此版本将使用处理程序的 typemap 属性来执行映射,该属性在 __init__() 被设置为一个字典,其中包含 DEBUG, INFO, WARNING, ERRORCRITICAL 的映射。 如果你使用你自己的级别,你将需要重载此方法或者在处理程序的 typemap 属性中放置一个合适的字典。

  • getMessageID(record)

    返回记录的消息 ID。 如果你使用你自己的消息,你可以通过将 msg 传给日志记录器作为 ID 而非格式字符串实现此功能。 然后,你可以在这里使用字典查找来获取消息 ID。 此版本将返回 1,是 win32service.pyd 中的基本消息 ID。

SMTPHandler

SMTPHandler 类位于 logging.handlers 模块,它支持将日志记录消息通过 SMTP 发送到一个电子邮件地址。

class logging.handlers.SMTPHandler(mailhost, fromaddr, toaddrs, subject, credentials=None, secure=None, timeout=1.0)

返回一个 SMTPHandler 类的新实例。 该实例使用电子邮件的发件人、收件人地址和主题行进行初始化。 toaddrs 应当为字符串列表。 要指定一个非标准 SMTP 端口,请使用 (host, port) 元组格式作为 mailhost 参数。 如果你使用一个字符串,则会使用标准 SMTP 端口。 如果你的 SMTP 服务器要求验证,你可以指定一个 (username, password) 元组作为 credentials 参数。

要指定使用安全协议 (TLS),请传入一个元组作为 secure 参数。 这将仅在提供了验证凭据时才能被使用。 元组应当或是一个空元组,或是一个包含密钥文件名的单值元组,或是一个包含密钥文件和证书文件的 2 值元组。 (此元组会被传给 smtplib.SMTP.starttls() 方法。)

可以使用 timeout 参数为与 SMTP 服务器的通信指定超时限制。

3.3 新版功能: 增加了 timeout 参数。

  • emit(record)

    对记录执行格式化并将其发送到指定的地址。

  • getSubject(record)

    如果你想要指定一个基于记录的主题行,请重载此方法。

MemoryHandler

MemoryHandler 类位于 logging.handlers 模块,它支持在内存中缓冲日志记录,并定期将其刷新到 target 处理程序中。 刷新会在缓冲区满的时候,或是在遇到特定或更高严重程度事件的时候发生。

MemoryHandler 是更通用的 BufferingHandler 的子类,后者属于抽象类。 它会在内存中缓冲日志记录。 当每条记录被添加到缓冲区时,会通过调用 shouldFlush() 来检查缓冲区是否应当刷新。 如果应当刷新,则要使用 flush() 来执行刷新。

class logging.handlers.BufferingHandler(capacity)

使用指定容量的缓冲区初始化处理程序。 这里,capacity 是指缓冲的日志记录数量。

  • emit(record)

    将记录添加到缓冲区。 如果 shouldFlush() 返回真值,则会调用 flush() 来处理缓冲区。

  • flush()

    你可以重载此方法来实现自定义的刷新行为。 此版本只是将缓冲区清空。

  • shouldFlush(record)

    如果缓冲区容量已满则返回 True。 可以重载此方法以实现自定义的刷新策略。

class logging.handlers.MemoryHandler(capacity, flushLevel=ERROR, target=None, flushOnClose=True)

返回一个 MemoryHandler 类的新实例。 该实例使用 capacity 指定的缓冲区大小(要缓冲的记录数量)来初始化。 如果 flushLevel 未指定,则使用 ERROR。 如果未指定 target*,则需要在此处理程序执行任何实际操作之前使用 setTarget() 来设置目标。 如果 *flushOnClose 指定为 False,则当处理程序被关闭时 不会 刷新缓冲区。 如果未指定或指定为 True,则当处理程序被关闭时将会发生之前的缓冲区刷新行为。

在 3.6 版更改: 增加了 flushOnClose 形参。

  • close()

    调用 flush(),设置目标为 None 并清空缓冲区。

  • flush()

    对于 MemoryHandler,刷新是指将缓冲的记录发送到目标,如果存在目标的话。 当此行为发生时缓冲区也将被清空。 如果你想要不同的行为请重载此方法。

  • setTarget(target)

    设置此处理程序的目标处理程序。

  • shouldFlush(record)

    检测缓冲区是否已满或是有记录为 flushLevel 或更高级别。

HTTPHandler

HTTPHandler 类位于 logging.handlers 模块,它支持使用 GETPOST 语义将日志记录消息发送到 Web 服务器。

class logging.handlers.HTTPHandler(host, url, method=’GET’, secure=False, credentials=None, context=None)

返回一个 HTTPHandler 类的新实例。 host 可以为 host:port 的形式,如果你需要使用指定端口号的话。 如果没有指定 method*,则会使用 GET。 如果 *secure 为真值,则将使用 HTTPS 连接。 context 形参可以设为一个 ssl.SSLContext 实例以配置用于 HTTPS 连接的 SSL 设置。 如果指定了 credentials,它应当为包含 userid 和 password 的二元组,该元组将被放入使用 Basic 验证的 HTTP ‘Authorization’ 标头中。 如果你指定了 credentials,你还应当指定 secure=True 这样你的 userid 和 password 就不会以明文在线路上传输。

在 3.5 版更改: 增加了 context 形参。

  • mapLogRecord(record)

    基于 record 提供一个字典,它将被执行 URL 编码并发送至 Web 服务器。 默认实现仅返回 record.__dict__。 在只需将 LogRecord 的某个子集发送至 Web 服务器,或者需要对发送至服务器的内容进行更多定制时可以重载此方法。

  • emit(record)

    将记录以 URL 编码字典的形式发送至 Web 服务器。 mapLogRecord() 方法会被用来将要发送的记录转换为字典。

注解

由于记录发送至 Web 服务器所需的预处理与通用的格式化操作不同,使用 setFormatter() 来指定一个 Formatter 用于 HTTPHandler 是没有效果的。 此处理程序不会调用 format(),而是调用 mapLogRecord() 然后再调用 urllib.parse.urlencode() 来以适合发送至 Web 服务器的形式对字典进行编码。

QueueHandler

3.2 新版功能.

QueueHandler 类位于 logging.handlers 模块,它支持将日志记录消息发送到一个队列,例如在 queuemultiprocessing 模块中实现的队列。

配合 QueueListener 类使用,QueueHandler 可被用来使处理程序在与执行日志记录的线程不同的线程上完成工作。 这对 Web 应用程序以及其他服务于客户端的线程需要尽可能快地响应的服务应用程序来说很重要,任何潜在的慢速操作(例如通过 SMTPHandler 发送邮件)都要在单独的线程上完成。

class logging.handlers.QueueHandler(queue)

返回一个 QueueHandler 类的新实例。 该实例使用队列来初始化以向其发送消息。 queue 可以为任何队列类对象;它由 enqueue() 方法来使用,该方法需要知道如何向其发送消息。 队列 不要求 具有任务跟踪 API,这意味着你可以为 queue 使用 SimpleQueue 实例。

  • emit(record)

    将准备 LogRecord 的结果排入队列。 如果发生了异常(例如由于有界队列已满),则会调用 handleError() 方法来处理错误。 这可能导致记录被静默地丢弃 (如果 logging.raiseExceptionsFalse) 或者消息被打印到 sys.stderr (如果 logging.raiseExceptionsTrue)。

  • prepare(record)

    准备用于队列的记录。 此方法返回的对象会被排入队列。

    基本实现会格式化记录以合并消息、参数以及可能存在的异常信息。 它还会从记录中原地移除无法封存的条目。

    如果你想要将记录转换为 dict 或 JSON 字符串,或者发送记录被修改后的副本而让初始记录保持原样,则你可能会想要重载此方法。

  • enqueue(record)

    使用 put_nowait() 将记录排入队列;如果你想要使用阻塞行为,或超时设置,或自定义的队列实现,则你可能会想要重载此方法。

QueueListener

3.2 新版功能.

QueueListener 类位于 logging.handlers 模块,它支持从一个队列接收日志记录消息,例如在 queuemultiprocessing 模块中实现的队列。 消息是在内部线程中从队列接收并在同一线程上传递到一个或多个处理程序进行处理的。 尽管 QueueListener 本身并不是一个处理程序,但由于它要与 QueueHandler 配合工作,因此也在此处介绍。

配合 QueueHandler 类使用,QueueListener 可被用来使处理程序在与执行日志记录的线程不同的线程上完成工作。 这对 Web 应用程序以及其他服务于客户端的线程需要尽可能快地响应的服务应用程序来说很重要,任何潜在的慢速动作(例如通过 SMTPHandler 发送邮件)都要在单独的线程上完成。

class logging.handlers.QueueListener(queue, \handlers, respect_handler_level=False*)

返回一个 QueueListener 类的新实例。 该实例初始化时要传入一个队列以向其发送消息,还要传入一个处理程序列表用来处理放置在队列中的条目。 队列可以是任何队列类对象;它会被原样传给 dequeue() 方法,该方法需要知道如何从其获取消息。 队列 不要求 具有任务跟踪 API(但如提供则会使用它),这意味着你可以为 queue 使用 SimpleQueue 实例。

如果 respect_handler_levelTrue,则在决定是否将消息传递给处理程序之前会遵循处理程序的级别(与消息的级别进行比较);在其他情况下,其行为与之前的 Python 版本一致 —— 总是将每条消息传递给每个处理程序。

在 3.5 版更改: 增加了 respect_handler_level 参数。

  • dequeue(block)

    从队列移出一条记录并将其返回,可以选择阻塞。

    基本实现使用 get()。 如果你想要使用超时设置或自定义的队列实现,则你可能会想要重载此方法。

  • prepare(record)

    准备一条要处理的记录。

    该实现只是返回传入的记录。 如果你想要对记录执行任何自定义的 marshal 操作或在将其传给处理程序之前进行调整,则你可能会想要重载此方法。

  • handle(record)

    处理一条记录。

    此方法简单地循环遍历处理程序,向它们提供要处理的记录。 实际传给处理程序的对象就是从 prepare() 返回的对象。

  • start()

    启动监听器。

    此方法启动一个后台线程来监视 LogRecords 队列以进行处理。

  • stop()

    停止监听器。

    此方法要求线程终止,然后等待它完成终止操作。 请注意在你的应用程序退出之前如果你没有调用此方法,则可能会有一些记录在留在队列中,它们将不会被处理。

  • enqueue_sentinel()

    将一个标记写入队列以通知监听器退出。 此实现会使用 put_nowait()。 如果你想要使得超时设置或自定义的队列实现,则你可能会想要重载此方法。

    3.3 新版功能.

getpass —- 便携式密码输入工具

源代码: Lib/getpass.py


getpass 模块提供了两个函数:

getpass.getpass(prompt=’Password: ‘, stream=None)

提示用户输入一个密码且不会回显。 用户会看到字符串 prompt 作为提示,其默认值为 'Password: '。 在 Unix 上,如有必要提示会使用替换错误句柄写入到文件类对象 stream*。 *stream 默认指向控制终端 (/dev/tty),如果不可用则指向 sys.stderr (此参数在 Windows 上会被忽略)。

如果回显自由输入不可用则 getpass() 将回退为打印一条警告消息到 stream 并且从 sys.stdin 读取同时发出 GetPassWarning

注解

如果你从 IDLE 内部调用 getpass,输入可能是在你启动 IDLE 的终端中而非在 IDLE 窗口本身中完成。

exception getpass.GetPassWarning

一个当密码输入可能被回显时发出的 UserWarning 子类。

getpass.getuser()

返回用户的“登录名称”。

此函数会按顺序检查环境变量 LOGNAME, USER, LNAMEUSERNAME,并返回其中第一个被设置为非空字符串的值。 如果均未设置,则在支持 pwd 模块的系统上将返回来自密码数据库的登录名,否则将引发一个异常。

通常情况下,此函数应优先于 os.getlogin() 使用。

curses —- 终端字符单元显示的处理

curses 模块提供了 curses 库的接口,这是可移植高级终端处理的事实标准。

虽然 curses 在 Unix 环境中使用最为广泛,但也有适用于 Windows,DOS 以及其他可能的系统的版本。此扩展模块旨在匹配 ncurses 的 API,这是一个部署在 Linux 和 Unix 的 BSD 变体上的开源 curses 库。

注解

每当文档提到 字符 时,它可以被指定为一个整数,一个单字符 Unicode 字符串或者一个单字节的字节字符串。

每当此文档提到 字符串 时,它可以被指定为一个 Unicode 字符串或者一个字节字符串。

注解

从 5.4 版本开始,ncurses 库使用 nl_langinfo 函数来决定如何解释非 ASCII 数据。这意味着你需要在程序中调用 locale.setlocale() 函数,并使用一种系统中可用的编码方法来编码 Unicode 字符串。这个例子使用了系统默认的编码:

import locale
locale.setlocale(locale.LC_ALL, '')
code = locale.getpreferredencoding()

然后使用 code 作为 str.encode() 调用的编码。

关于配合 Python 使用 curses 的教学材料,由 Andrew Kuchling 和 Eric Raymond 撰写。

Python 源码发布包的 Tools/demo/ 目录包含了一些使用此模块所提供的 curses 绑定的示例程序。

函数

curses 模块定义了以下异常:

exception curses.error

当 curses 库中函数返回一个错误时引发的异常。

注解

只要一个函数或方法的 xy 参数是可选项,它们会默认为当前光标位置。 而当 attr 是可选项时,它会默认为 A_NORMAL

curses 模块定义了以下函数:

curses.baudrate()

以每秒比特数为单位返回终端输出速度。 在软件终端模拟器上它将具有一个固定的最高值。 此函数出于历史原因被包括;在以前,它被用于写输出循环以提供时间延迟,并偶尔根据线路速度来改变接口。

curses.beep()

发出短促的提醒声音。

curses.can_change_color()

根据程序员能否改变终端显示的颜色返回 TrueFalse

curses.cbreak()

进入 cbreak 模式。 在 cbreak 模式(有时也称为“稀有”模式)通常的 tty 行缓冲会被关闭并且字符可以被一个一个地读取。 但是,与原始模式不同,特殊字符(中断、退出、挂起和流程控制)会在 tty 驱动和调用程序上保留其效果。 首先调用 raw() 然后调用 cbreak() 会将终端置于 cbreak 模式。

curses.color_content(color_number)

返回颜色值 color_number 中红、绿和蓝(RGB)分量的强度,此强度值必须介于 0COLORS - 1 之间。 返回一个 3 元组,其中包含给定颜色的 R,G,B 值,它们必须介于 0 (无分量) 和 1000 (最大分量) 之间。

curses.color_pair(pair_number)

返回用于以指定颜色对显示文本的属性值。 仅支持前 256 个颜色对。 该属性值可与 A_STANDOUT, A_REVERSE 以及其他 A_* 属性组合使用。 pair_number() 是此函数的对应操作。

curses.curs_set(visibility)

设置光标状态。 visibility 可设为 0, 12 表示不可见、正常与高度可见。 如果终端支持所请求的可见性,则返回之前的光标状态;否则会引发异常。 在许多终端上,“正常可见”模式为下划线光标而“高度可见”模式为方块形光标。

curses.def_prog_mode()

将当前终端模式保存为 “program” 模式,即正在运行的程序使用 curses 的模式。 (与其相对的是 “shell” 模式,即程序不使用 curses。) 对 reset_prog_mode() 的后续调用将恢复此模式。

curses.def_shell_mode()

将当前终端模式保存为 “shell” 模式,即正在运行的程序不使用 curses 的模式。 (与其相对的是 “program” 模式,即程序使用 功能。) 对 reset_shell_mode() 的后续调用将恢复此模式。

curses.delay_output(ms)

在输出中插入 ms 毫秒的暂停。

curses.doupdate()

更新物理屏幕。 curses 库会保留两个数据结构,一个代表当前物理屏幕的内容以及一个虚拟屏幕代表需要的后续状态。 doupdate() 整体更新物理屏幕以匹配虚拟屏幕。

虚拟屏幕可以通过在写入操作例如在一个窗口上执行 addstr() 之后调用 noutrefresh() 来刷新。 普通的 refresh() 调用只是简单的 noutrefresh()doupdate();如果你需要更新多个窗口,你可以通过在所有窗口上发出 noutrefresh() 调用再加单次 doupdate() 来提升性能并可减少屏幕闪烁。

curses.echo()

进入 echo 模式。 在 echo 模式下,输入的每个字符都会在输入后回显到屏幕上。

curses.endwin()

撤销库的初始化,使终端返回正常状态。

curses.erasechar()

将用户的当前擦除字符以单字节字节串对象的形式返回。 在 Unix 操作系统下这是 curses 程序用来控制 tty 的属性,而不是由 curses 库本身来设置的。

curses.filter()

如果要使用 filter() 例程,它必须在调用 initscr() 之前被调用。 其效果是在这些调用期间,LINES 会被设为 1clear, cup, cud, cud1, cuu1, cuu, vpa 等功能会被禁用;而 home 字符串会被设为 cr 的值。 其影响是光标会被限制在当前行内,屏幕刷新也是如此。 这可被用于启用单字符模式的行编辑而不触及屏幕的其余部分。

curses.flash()

闪烁屏幕。 也就是将其改为反显并在很短的时间内将其改回原状。 有些人更喜欢这样的‘视觉响铃’而非 beep() 所产生的听觉提醒信号。

curses.flushinp()

刷新所有输入缓冲区。 这会丢弃任何已被用户输入但尚未被程序处理的预输入内容。

curses.getmouse()

getch() 返回 KEY_MOUSE 以发出鼠标事件信号之后,应当调用此方法来获取加入队列的鼠标事件,事件以一个 5 元组 (id, x, y, z, bstate) 来表示。 其中 id 为用于区分多个设备的 ID 值,而 x, y, z 为事件的坐标。 (z 目前未被使用。) bstate 为一个整数值,其各个比特位将被设置用来表示事件的类型,并将为下列常量中的一个或多个按位 OR 的结果,其中 n 是以 1 到 5 表示的键号: BUTTONn_PRESSED, BUTTONn_RELEASED, BUTTONn_CLICKED, BUTTONn_DOUBLE_CLICKED, BUTTONn_TRIPLE_CLICKED, BUTTON_SHIFT, BUTTON_CTRL, BUTTON_ALT

在 3.10 版更改: 现在 BUTTON5_* 常量如果是由下层 curses 库提供的则会对外公开。

curses.getsyx()

将当前虚拟屏幕光标的坐标作为元组 (y, x) 返回。 如果 leaveok 当前为 True,则返回 (-1, -1)

curses.getwin(file)

读取由之前的 putwin() 调用存放在文件中的窗口相关数据。 该例程随后将使用该数据创建并初始化一个新窗口,并返回该新窗口对象。

curses.has_colors()

如果终端能显示彩色则返回 True;否则返回 False

curses.has_extended_color_support()

如果此模块支持扩展颜色则返回 True;否则返回 False。 扩展颜色支持允许支持超过 16 种颜色的终端(例如 xterm-256color)支持超过 256 种颜色对。

扩展颜色支持要求 ncurses 版本为 6.1 或更新。

3.10 新版功能.

curses.has_ic()

如果终端具有插入和删除字符的功能则返回 True。 此函数仅是出于历史原因而被包括的,因为所有现代软件终端模拟器都具有这些功能。

curses.has_il()

如果终端具有插入和删除字符功能,或者能够使用滚动区域来模拟这些功能则返回 True。 此函数仅是出于历史原因而被包括的,因为所有现代软件终端模拟器都具有这些功能。

curses.has_key(ch)

接受一个键值 ch,并在当前终端类型能识别出具有该值的键时返回 True

curses.halfdelay(tenths)

用于半延迟模式,与 cbreak 模式的类似之处是用户所键入的字符会立即对程序可用。 但是,在阻塞 tenths 个十分之一秒之后,如果还未输入任何内容则将引发异常。 tenths 值必须为 1255 之间的数字。 使用 nocbreak() 可退出半延迟模式。

curses.init_color(color_number, r, g, b)

更改某个颜色的定义,接受要更改的颜色编号以及三个 RGB 值(表示红绿蓝三分量的强度)。 color_number 值必须为 0 和 COLORS - 1 之间的数字。 r, g, b 值必须为 01000 之间的数字。 当使用 init_color() 时,出现在屏幕上的对应颜色会立即按照新定义来更改。 此函数在大多数终端上都是无操作的;它仅会在 can_change_color() 返回 True 时生效。

curses.init_pair(pair_number, fg, bg)

更改某个颜色对的定义。 它接受三个参数:要更改的颜色对编号,前景色编号和背景色编号。 pair_number 值必须为 1COLOR_PAIRS - 1 之间的数字(并且 0 号颜色对固定为黑底白字而无法更改)。 fgbg 参数必须为 0COLORS - 1 之间的数字,或者在调用 use_default_colors() 之后则为 -1。 如果颜色对之前已被初始化,则屏幕会被刷新使得出现在屏幕上的该颜色会立即按照新定义来更改。

curses.initscr()

初始化库。 返回代表整个屏幕的 窗口 对象。

注解

如果打开终端时发生错误,则下层的 curses 库可能会导致解释器退出。

curses.is_term_resized(nlines, ncols)

如果 resize_term() 会修改窗口结构则返回 True,否则返回 False

curses.isendwin()

如果 endwin() 已经被调用(即 curses 库已经被撤销初始化则返回 True

curses.keyname(k)

将编号为 k 的键名称作为字节串对象返回。 生成可打印 ASCII 字符的键名称就是键所对应的字符。 Ctrl-键组合的键名称则是一个两字节的字节串对象,它由插入符 (b'^') 加对应的可打印 ASCII 字符组成。 Alt-键组合 (128—255) 的键名称则是由前缀 b'M-' 加对应的可打印 ASCII 字符组成的字节串对象。

curses.killchar()

将用户的当前行删除字符以单字节字节串对象的形式返回。 在 Unix 操作系统下这是 curses 程序用来控制 tty 的属性,而不是由 curses 库本身来设置的。

curses.longname()

返回一个字节串对象,其中包含描述当前终端的 terminfo 长名称字段。 详细描述的最大长度为 128 个字符。 它仅在调用 initscr() 之后才会被定义。

curses.meta(flag)

如果 flagTrue,则允许输入 8 比特位的字符。 如果 flagFalse,则只允许 7 比特位的字符。

curses.mouseinterval(interval)

以毫秒为单位设置能够被识别为点击的按下和释放事件之间可以间隔的最长时间,并返回之前的间隔值。 默认值为 200 毫秒,即五分之一秒。

curses.mousemask(mousemask)

设置要报告的鼠标事件,并返回一个元组 (availmask, oldmask)availmask 表明指定的鼠标事件中哪些可以被报告;当完全失败时将返回 0oldmask 是给定窗口的鼠标事件之前的掩码值。 如果从未调用此函数,则不会报告任何鼠标事件。

curses.napms(ms)

休眠 ms 毫秒。

curses.newpad(nlines, ncols)

创建并返回一个指向具有给定行数和列数新的面板数据结构的指针。 将面板作为窗口对象返回。

面板类似于窗口,区别在于它不受屏幕大小的限制,并且不必与屏幕的特定部分相关联。 面板可以在需要使用大窗口时使用,并且每次只需将窗口的一部分放在屏幕上。 面板不会发生自动刷新(例如由于滚动或输入回显)。 面板的 refresh()noutrefresh() 方法需要 6 个参数来指定面板要显示的部分以及要用于显示的屏幕位置。 这些参数是 pminrow, pmincol, sminrow, smincol, smaxrow, smaxcolp 参数表示要显示的面板区域的左上角而 s 参数定义了要显示的面板区域在屏幕上的剪切框。

curses.newwin(nlines, ncols)

curses.newwin(nlines, ncols, begin_y, begin_x)

返回一个新的 窗口,其左上角位于 (begin_y, begin_x),并且其高度/宽度为 nlines/ncols

默认情况下,窗口将从指定位置扩展到屏幕的右下角。

curses.nl()

进入 newline 模式。 此模式会在输入时将回车转换为换行符,并在输出时将换行符转换为回车加换行。 newline 模式会在初始时启用。

curses.nocbreak()

退出 cbreak 模式。 返回具有行缓冲的正常 “cooked” 模式。

curses.noecho()

退出 echo 模式。 关闭输入字符的回显。

curses.nonl()

退出 newline 模式。 停止在输入时将回车转换为换行,并停止在输出时从换行到换行/回车的底层转换(但这不会改变 addch('\n') 的行为,此行为总是在虚拟屏幕上执行相当于回车加换行的操作)。 当停止转换时,curses 有时能使纵向移动加快一些;并且,它将能够在输入时检测回车键。

curses.noqiflush()

当使用 noqiflush() 例程时,与 INTR, QUITSUSP 字符相关联的输入和输出队列的正常刷新将不会被执行。 如果你希望在处理程序退出后还能继续输出,就像没有发生过中断一样,你可能会想要在信号处理程序中调用 noqiflush()

curses.noraw()

退出 raw 模式。 返回具有行缓冲的正常 “cooked” 模式。

curses.pair_content(pair_number)

返回包含对应于所请求颜色对的元组 (fg, bg)pair_number 的值必须在 0COLOR_PAIRS - 1 之间。

curses.pair_number(attr)

返回通过属性值 attr 所设置的颜色对的编号。 color_pair() 是此函数的对应操作。

curses.putp(str)

等价于 tputs(str, 1, putchar);为当前终端发出指定 terminfo 功能的值。 请注意 putp() 的输出总是前往标准输出。

curses.qiflush([flag])

如果 flagFalse,则效果与调用 noqiflush() 相同。 如果 flagTrue 或未提供参数,则在读取这些控制字符时队列将被刷新。

curses.raw()

进入 raw 模式。 在 raw 模式下,正常的行缓冲和对中断、退出、挂起和流程控制键的处理会被关闭;字符会被逐个地提交给 curses 输入函数。

curses.reset_prog_mode()

将终端恢复到 “program” 模式,如之前由 def_prog_mode() 所保存的一样。

curses.reset_shell_mode()

将终端恢复到 “shell” 模式,如之前由 def_shell_mode() 所保存的一样。

curses.resetty()

将终端模式恢复到最后一次调用 savetty() 时的状态。

curses.resize_term(nlines, ncols)

resizeterm() 用来执行大部分工作的后端函数;当调整窗口大小时,resize_term() 会以空白填充扩展区域。 调用方应用程序应当以适当的数据填充这些区域。 resize_term() 函数会尝试调整所有窗口的大小。 但是,由于面板的调用约定,在不与应用程序进行额外交互的情况下是无法调整其大小的。

curses.resizeterm(nlines, ncols)

将标准窗口和当前窗口的大小调整为指定的尺寸,并调整由 curses 库所使用的记录窗口尺寸的其他记录数据(特别是 SIGWINCH 处理程序)。

curses.savetty()

将终端模式的当前状态保存在缓冲区中,可供 resetty() 使用。

curses.get_escdelay()

提取通过 set_escdelay() 设置的值。

3.9 新版功能.

curses.set_escdelay(ms)

设置读取一个转义字符后要等待的毫秒数,以区分在键盘上输入的单个转义字符与通过光标和功能键发送的转义序列。

3.9 新版功能.

curses.get_tabsize()

提取通过 set_tabsize() 设置的值。

3.9 新版功能.

curses.set_tabsize(size)

设置 curses 库在将制表符添加到窗口时将制表符转换为空格所使用的列数。

3.9 新版功能.

curses.setsyx(y, x)

将虚拟屏幕光标设置到 y, x。 如果 yx 均为 -1,则 leaveok 将设为 True

curses.setupterm(term=None, fd=- 1)

初始化终端。 term 为给出终端名称的字符串或为 None;如果省略或为 None,则将使用 TERM 环境变量的值。 fd 是任何初始化序列将被发送到的文件描述符;如未指定或为 -1,则将使用 sys.stdout 的文件描述符。

curses.start_color()

如果程序员想要使用颜色,则必须在任何其他颜色操作例程被调用之前调用它。 在 initscr() 之后立即调用此例程是一个很好的做法。

start_color() 会初始化八种基本颜色(黑、红、绿、黄、蓝、品、青和白)以及 curses 模块中的两个全局变量 COLORSCOLOR_PAIRS,其中包含终端可支持的颜色和颜色对的最大数量。 它还会将终端中的颜色恢复为终端刚启动时的值。

curses.termattrs()

返回终端所支持的所有视频属性逻辑 OR 的值。 此信息适用于当 curses 程序需要对屏幕外观进行完全控制的情况。

curses.termname()

将环境变量 TERM 的值截短至 14 个字节,作为字节串对象返回。

curses.tigetflag(capname)

将与 terminfo 功能名称 capname 相对应的布尔功能值以整数形式返回。 如果 capname 不是一个布尔功能则返回 -1,如果其被取消或不存在于终端描述中则返回 0

curses.tigetnum(capname)

将与 terminfo 功能名称 capname 相对应的数字功能值以整数形式返回。 如果 capname 不是一个数字功能则返回 -2,如果其被取消或不存在于终端描述中则返回 -1

curses.tigetstr(capname)

将与 terminfo 功能名称 capname 相对应的字符串功能值以字节串对象形式返回。 如果 capname 不是一个 terminfo “字符串功能” 或者如果其被取消或不存在于终端描述中则返回 None

curses.tparm(str[, ])

使用提供的形参初始化字节串对象 str*,其中 *str 应当是从 terminfo 数据库获取的参数化字符串。 例如 tparm(tigetstr("cup"), 5, 3) 的结果可能为 b'\033[6;4H',实际结果将取决于终端类型。

curses.typeahead(fd)

指定将被用于预输入检查的文件描述符 fd*。 如果 *fd-1,则不执行预输入检查。

curses 库会在更新屏幕时通过定期查找预输入来执行 “断行优化”。 如果找到了输入,并且输入是来自于 tty,则会将当前更新推迟至 refresh 或 doupdate 再次被调用的时候,以便允许更快地响应预先输入的命令。 此函数允许为预输入检查指定其他的文件描述符。

curses.unctrl(ch)

返回一个字节串对象作为字符 ch 的可打印表示形式。 控制字符会表示为一个变换符加相应的字符,例如 b'^C'。 可打印字符则会保持原样。

curses.ungetch(ch)

推送 ch 以便让下一个 getch() 返回该字符。

注解

getch() 被调用之前只能推送一个 ch

curses.update_lines_cols()

更新 LINESCOLS。 适用于检测屏幕大小的手动调整。

3.5 新版功能.

curses.unget_wch(ch)

推送 ch 以便让下一个 get_wch() 返回该字符。

注解

get_wch() 被调用之前只能推送一个 ch

3.3 新版功能.

curses.ungetmouse(id, x, y, z, bstate)

KEY_MOUSE 事件推送到输入队列,将其与给定的状态数据进行关联。

curses.use_env(flag)

如果使用此函数,则应当在调用 initscr() 或 newterm 之前调用它。 当 flagFalse 时,将会使用在 terminfo 数据库中指定的行和列的值,即使设置了环境变量 LINESCOLUMNS (默认使用),或者如果 curses 是在窗口中运行(在此情况下如果未设置 LINESCOLUMNS 则默认行为将是使用窗口大小)。

curses.use_default_colors()

允许在支持此特性的终端上使用默认的颜色值。 使用此函数可在你的应用程序中支持透明效果。 默认颜色会被赋给颜色编号 -1。 举例来说,在调用此函数后,init_pair(x, curses.COLOR_RED, -1) 会将颜色对 x 初始化为红色前景和默认颜色背景。

curses.wrapper(func, /, \args, *kwargs)

初始化 curses 并调用另一个可调用对象 func*,该对象应当为你的使用 curses 的应用程序的其余部分。 如果应用程序引发了异常,此函数将在重新引发异常并生成回溯信息之前将终端恢复到正常状态。 随后可调用对象 *func 会被传入主窗口 ‘stdscr’ 作为其第一个参数,再带上其他所有传给 wrapper() 的参数。 在调用 func 之前,wrapper() 会启用 cbreak 模式,关闭回显,启用终端键盘,并在终端具有颜色支持的情况下初始化颜色。 在退出时(无论是正常退出还是异常退出)它会恢复 cooked 模式,打开回显,并禁用终端键盘。

Window 对象

Window 对象会由上面的 initscr()newwin() 返回,它具有以下方法和属性:

window.addch(ch[, attr])

window.addch(y, x, ch[, attr])

将带有属性 attr 的字符 ch 绘制到 (y, x),覆盖之前在该位置上绘制的任何字符。 默认情况下,字符的位置和属性均为窗口对象的当前设置。

注解

在窗口、子窗口或面板之外写入会引发 curses.error。 尝试在窗口、子窗口或面板的右下角写入将在字符被打印之后导致异常被引发。

window.addnstr(str, n[, attr])

window.addnstr(y, x, str, n[, attr])

将带有属性 attr 的字符串 str 中的至多 n 个字符绘制到 (y, x),覆盖之前在屏幕上的任何内容。

window.addstr(str[, attr])

window.addstr(y, x, str[, attr])

将带有属性 attr 的字符串 str 绘制到 (y, x),覆盖之前在屏幕上的任何内容。

注解

  • 在窗口、子窗口或面板之外写入会引发 curses.error。 尝试在窗口、子窗口或面板的右下角写入将在字符串被打印之后导致异常被引发。
  • 此 Python 模块的后端 ncurses 中的一个缺陷 会在调整窗口大小时导致段错误。 此缺陷已在 ncurses-6.1-20190511 中被修复。 如果你必须使用较早版本的 ncurses,则你只要在调用 addstr() 时不传入嵌入了换行符的 str 即可避免触发此错误。 请为每一行分别调用 addstr()

window.attroff(attr)

从应用于写入到当前窗口的 “background” 集中移除属性 attr

window.attron(attr)

向应用于写入到当前窗口的 “background” 集中添加属性 attr

window.attrset(attr)

将 “background” 属性集设为 attr。 该集合初始时为 0 (无属性)。

window.bkgd(ch[, attr])

将窗口 background 特征属性设为带有属性 attr 的字符 ch。 随后此修改将应用于放置到该窗口中的每个字符。

  • 窗口中每个字符的属性会被修改为新的 background 属性。
  • 不论之前的 background 字符出现在哪里,它都会被修改为新的 background 字符。

window.bkgdset(ch[, attr])

设置窗口的背景。 窗口的背景由字符和属性的任意组合构成。 背景的属性部分会与写入窗口的所有非空白字符合并(即 OR 运算)。 背景和字符和属性部分均会与空白字符合并。 背景将成为字符的特征属性并在任何滚动与插入/删除行/字符操作中与字符一起移动。

window.border([ls[, rs[, ts[, bs[, tl[, tr[, bl[, br]]]]]]]])

在窗口边缘绘制边框。每个参数指定用于边界特定部分的字符;请参阅下表了解更多详情。

注解

任何形参的值为 0 都将导致该形参使用默认字符。 关键字形参 不可 被使用。 默认字符在下表中列出:

参数 描述 默认值
ls 左侧 ACS_VLINE
rs 右侧 ACS_VLINE
ts 顶部 ACS_HLINE
bs 底部 ACS_HLINE
tl 左上角 ACS_ULCORNER
tr 右上角 ACS_URCORNER
bl 左下角 ACS_LLCORNER
br 右下角 ACS_LRCORNER

window.box([vertch, horch])

类似于 border(),但 lsrs 均为 vertchtsbs 均为 horch。 此函数总是会使用默认的转角字符。

window.chgat(attr)

window.chgat(num, attr)

window.chgat(y, x, attr)

window.chgat(y, x, num, attr)

在当前光标位置或是在所提供的位置 (y, x) 设置 num 个字符的属性。 如果 num 未给出或为 -1,则将属性设置到所有字符上直至行尾。 如果提供了位置 (y, x) 则此函数会将光标移至该位置。 修改过的行将使用 touchline() 方法处理以便下次窗口刷新时内容会重新显示。

window.clear()

类似于 erase(),但还会导致在下次调用 refresh() 时整个窗口被重新绘制。

window.clearok(flag)

如果 flagTrue,则在下次调用 refresh() 时将完全清除窗口。

window.clrtobot()

从光标位置开始擦除直至窗口末端:光标以下的所有行都会被删除,然后会执行 clrtoeol() 的等效操作。

window.clrtoeol()

从光标位置开始擦除直至行尾。

window.cursyncup()

更新窗口所有上级窗口的当前光标位置以反映窗口的当前光标位置。

window.delch([y, x])

删除位于 (y, x) 的任何字符。

window.deleteln()

删除在光标之下的行。 所有后续的行都会上移一行。

window.derwin(begin_y, begin_x)

window.derwin(nlines, ncols, begin_y, begin_x)

“derive window” 的缩写,derw

in() 与调用 subwin() 等效,不同之处在于 begin_ybegin_x 是想对于窗口的初始位置,而不是相对于整个屏幕。 返回代表所派生窗口的窗口对象。

window.echochar(ch[, attr])

使用属性 attr 添加字符 ch,并立即在窗口上调用 refresh()

window.enclose(y, x)

检测给定的相对屏幕的字符-单元格坐标是否被给定的窗口所包围,返回 TrueFalse。 它适用于确定是哪个屏幕窗口子集包围着某个鼠标事件的位置。

在 3.10 版更改: 在之前版本中它会返回 10 而不是 TrueFalse

window.encoding

用于编码方法参数(Unicode 字符串和字符)的编码格式。 encoding 属性是在创建子窗口时从父窗口继承的,例如通过 window.subwin()。 默认情况下,会使用当前区域的编码格式 。

3.3 新版功能.

window.erase()

清空窗口。

window.getbegyx()

返回左上角坐标的元组 (y, x)

window.getbkgd()

返回给定窗口的当前背景字符/属性对。

window.getch([y, x])

获取一个字符。 请注意所返回的整数 不一定 要在 ASCII 范围以内:功能键、小键盘键等等是由大于 255 的数字表示的。 在无延迟模式下,如果没有输入则返回 -1,在其他情况下都会等待直至有键被按下。

window.get_wch([y, x])

获取一个宽字符。 对于大多数键都是返回一个字符,对于功能键、小键盘键和其他特殊键则是返回一个整数。 在无延迟模式下,如果没有输入则引发一个异常。

3.3 新版功能.

window.getkey([y, x])

获取一个字符,返回一个字符串而不是像 getch() 那样返回一个整数。 功能键、小键盘键和其他特殊键则是返回一个包含键名的多字节字符串。 在无延迟模式下,如果没有输入则引发一个异常。

window.getmaxyx()

返回窗口高度和宽度的元组 (y, x)

window.getparyx()

将此窗口相对于父窗口的起始坐标作为元组 (y, x) 返回。 如果此窗口没有父窗口则返回 (-1, -1)

window.getstr()

window.getstr(n)

window.getstr(y, x)

window.getstr(y, x, n)

从用户读取一个字节串对象,附带基本的行编辑功能。

window.getyx()

返回当前光标相对于窗口左上角的位置的元组 (y, x)

window.hline(ch, n)

window.hline(y, x, ch, n)

显示一条起始于 (y, x) 长度为 n 个字符 ch 的水平线。

window.idcok(flag)

如果 flagFalse,curses 将不再考虑使用终端的硬件插入/删除字符功能;如果 flagTrue,则会启用字符插入和删除。 当 curses 首次初始化时,默认会启用字符插入/删除。

window.idlok(flag)

如果 flagTruecurses 将尝试使用硬件行编辑功能。 否则,行插入/删除会被禁用。

window.immedok(flag)

如果 flagTrue,窗口图像中的任何改变都会自动导致窗口被刷新;你不必再自己调用 refresh()。 但是,这可能会由于重复调用 wrefresh 而显著降低性能。 此选项默认被禁用。

window.inch([y, x])

返回窗口中给定位置上的字符。 下面的 8 个比特位是字符本身,上面的比特位则为属性。

window.insch(ch[, attr])

window.insch(y, x, ch[, attr])

将带有属性 attr 的字符 ch 绘制到 (y, x),将该行从位置 x 开始右移一个字符。

window.insdelln(nlines)

在指定窗口的当前行上方插入 nlines 行。 下面的 nlines 行将丢失。 对于 nlines 为负值的情况,则从光标下方的行开始删除 nlines 行,并将其余的行向上移动。 下面的 nlines 行会被清空。 当前光标位置将保持不变。

window.insertln()

在光标下方插入一个空行。 所有后续的行都会下移一行。

window.insnstr(str, n[, attr])

window.insnstr(y, x, str, n[, attr])

在光标下方的字符之前插入一个至多为 n 个字符的字符串(字符数量将与该行相匹配)。 如果 n 为零或负数,则插入整个字符串。 光标右边的所有字符将被右移,该行右端的字符将丢失。 光标位置将保持不变(在移到可能指定的 y, x 之后)。

window.insstr(str[, attr])

window.insstr(y, x, str[, attr])

在光标下方的字符之前插入一个字符串(字符数量将与该行相匹配)。 光标右边的所有字符将被右移,该行右端的字符将丢失。 光标位置将保持不变(在移到可能指定的 y, x 之后)。

window.instr([n])

window.instr(y, x[, n])

返回从窗口的当前光标位置,或者指定的 y, x 开始提取的字符所对应的字节串对象。 属性会从字符中去除。 如果指定了 ninstr() 将返回长度至多为 n 个字符的字符串(不包括末尾的 NUL)。

window.is_linetouched(line)

如果指定的行自上次调用 refresh() 后发生了改变则返回 True;否则返回 False。 如果 line 对于给定的窗口不可用则会引发 curses.error 异常。

window.is_wintouched()

如果指定的窗口自上次调用 refresh() 后发生了改变则返回 True;否则返回 False

window.keypad(flag)

如果 flagTrue,则某些键(小键盘键、功能键等)生成的转义序列将由 curses 来解析。 如果 flagFalse,转义序列将保持在输入流中的原样。

window.leaveok(flag)

如果 flagTrue,则在更新时光标将停留在原地,而不是在“光标位置”。 这将可以减少光标的移动。 在可能的情况下光标将变为不可见。

如果 flagFalse,光标在更新后将总是位于“光标位置”。

window.move(new_y, new_x)

将光标移至 (new_y, new_x)

window.mvderwin(y, x)

让窗口在其父窗口内移动。 窗口相对于屏幕的参数不会被更改。 此例程用于在屏幕的相同物理位置显示父窗口的不同部分。

window.mvwin(new_y, new_x)

移动窗口以使其左上角位于 (new_y, new_x)

window.nodelay(flag)

如果 flagTrue,则 getch() 将为非阻塞的。

window.notimeout(flag)

如果 flagTrue,则转义序列将不会发生超时。

如果 flagFalse,则在几毫秒之后,转义序列将不会被解析,并将保持在输入流中的原样。

window.noutrefresh()

标记为刷新但保持等待。 此函数会更新代表预期窗口状态的数据结构,但并不强制更新物理屏幕。 要完成后者,请调用 doupdate()

window.overlay(destwin[, sminrow, smincol, dminrow, dmincol, dmaxrow, dmaxcol])

将窗口覆盖在 destwin 上方。 窗口的大小不必相同,只有重叠的区域会被复制。 此复制是非破坏性的,这意味着当前背景字符不会覆盖掉 destwin 的旧内容。

为了获得对被复制区域的细粒度控制,可以使用 overlay() 的第二种形式。 sminrowsmincol 是源窗口的左上角坐标,而其他变量则在目标窗口中标记出一个矩形。

window.overwrite(destwin[, sminrow, smincol, dminrow, dmincol, dmaxrow, dmaxcol])

将窗口覆盖在 destwin 上方。 窗口的大小不必相同,此时只有重叠的区域会被复制。 此复制是破坏性的,这意味着当前背景字符会覆盖掉 destwin 的旧内容。

为了获得对被复制区域的细粒度控制,可以使用 overwrite() 的第二种形式。 sminrowsmincol 是源窗口的左上角坐标,而其他变量则在目标窗口中标记出一个矩形。

window.putwin(file)

将关联到窗口的所有数据写入到所提供的文件对象。 此信息可在以后使用 getwin() 函数来提取。

window.redrawln(beg, num)

指明从 beg 行开始的 num 个屏幕行已被破坏并且应当在下次 refresh() 调用时完全重绘。

window.redrawwin()

触碰整个窗口,以使其在下次 refresh() 调用时完全重绘。

window.refresh([pminrow, pmincol, sminrow, smincol, smaxrow, smaxcol])

立即更新显示(将实际屏幕与之前的绘制/删除方法进行同步)。

6 个可选参数仅在窗口为使用 newpad() 创建的面板时可被指定。 需要额外的形参来指定所涉及到的是面板和屏幕的哪一部分。 pminrowpmincol 指定要在面板中显示的矩形的左上角。 sminrow, smincol, smaxrowsmaxcol 指定要在屏幕中显示的矩形的边。 要在面板中显示的矩形的右下角是根据屏幕坐标计算出来的,由于矩形的大小必须相同。 两个矩形都必须完全包含在其各自的结构之内。 负的 pminrow, pmincol, sminrowsmincol 值会被视为将它们设为零值。

window.resize(nlines, ncols)

为 curses 窗口重新分配存储空间以将其尺寸调整为指定的值。 如果任一维度的尺寸大于当前值,则窗口的数据将以具有合并了当前背景渲染(由 bkgdset() 设置)的空白来填充。

window.scroll([lines=1])

将屏幕或滚动区域向上滚动 lines 行。

window.scrollok(flag)

控制当一个窗口的光标移出窗口或滚动区域边缘时会发生什么,这可能是在底端行执行换行操作,或者在最后一行输入最后一个字符导致的结果。 如果 flagFalse,光标会留在底端行。 如果 flagTrue,窗口会向上滚动一行。 请注意为了在终端上获得实际的滚动效果,还需要调用 idlok()

window.setscrreg(top, bottom)

设置从 top 行至 bottom 行的滚动区域。 所有滚动操作将在此区域中进行。

window.standend()

关闭 standout 属性。 在某些终端上此操作会有关闭所有属性的副作用。

window.standout()

启用属性 A_STANDOUT

window.subpad(begin_y, begin_x)

window.subpad(nlines, ncols, begin_y, begin_x)

返回一个子窗口,其左上角位于 (begin_y, begin_x),并且其宽度/高度为 ncols/nlines

window.subwin(begin_y, begin_x)

window.subwin(nlines, ncols, begin_y, begin_x)

返回一个子窗口,其左上角位于 (begin_y, begin_x),并且其宽度/高度为 ncols/nlines

默认情况下,子窗口将从指定位置扩展到窗口的右下角。

window.syncdown()

触碰已在上级窗口上被触碰的每个位置。 此例程由 refresh() 调用,因此几乎从不需要手动调用。

window.syncok(flag)

如果 flagTrue,则 syncup() 会在窗口发生改变的任何时候自动被调用。

window.syncup()

触碰已在窗口中被改变的此窗口的各个上级窗口中的所有位置。

window.timeout(delay)

为窗口设置阻塞或非阻塞读取行为。 如果 delay 为负值,则会使用阻塞读取(这将无限期地等待输入)。 如果 delay 为零,则会使用非阻塞读取,并且当没有输入在等待时 getch() 将返回 -1。 如果 delay 为正值,则 getch() 将阻塞 delay 毫秒,并且当此延时结束时仍无输入将返回 -1

window.touchline(start, count[, changed])

假定从行 start 开始的 count 行已被更改。 如果提供了 changed,它将指明是将受影响的行标记为已更改 (changed=True) 还是未更改 (changed=False)。

window.touchwin()

假定整个窗口已被更改,其目的是用于绘制优化。

window.untouchwin()

将自上次调用 refresh() 以来窗口中的所有行标记为未改变。

window.vline(ch, n)

window.vline(y, x, ch, n)

显示一条起始于 (y, x) 长度为 n 个字符 ch 的垂直线。

常量

curses 模块定义了以下数据成员:

curses.ERR

一些返回整数的 curses 例程,例如 getch(),在失败时将返回 ERR

curses.OK

一些返回整数的 curses 例程,例如 napms(),在成功时将返回 OK

curses.version

一个代表当前模块版本的字节串对象。 也作 __version__

curses.ncurses_version

一个具名元组,它包含构成 ncurses 库版本号的三个数字: major, minorpatch。 三个值均为整数。 三个值也可通过名称来访问,因此 curses.ncurses_version[0] 等价于 curses.ncurses_version.major,依此类推。

可用性:如果使用了 ncurses 库。

3.8 新版功能.

有些常量可用于指定字符单元属性。 实际可用的常量取决于具体的系统。

属性 含意
A_ALTCHARSET 备用字符集模式
A_BLINK 闪烁模式
A_BOLD 粗体模式
A_DIM 暗淡模式
A_INVIS 不可见或空白模式
A_ITALIC 斜体模式
A_NORMAL 正常属性
A_PROTECT 保护模式
A_REVERSE 反转背景色和前景色
A_STANDOUT 突出模式
A_UNDERLINE 下划线模式
A_HORIZONTAL 水平突出显示
A_LEFT 左高亮
A_LOW 底部高亮
A_RIGHT 右高亮
A_TOP 顶部高亮
A_VERTICAL 垂直突出显示
A_CHARTEXT 用于提取字符的位掩码

3.7 新版功能: A_ITALIC was added.

有几个常量可用于提取某些方法返回的相应属性。

位掩码 含意
A_ATTRIBUTES 用于提取属性的位掩码
A_CHARTEXT 用于提取字符的位掩码
A_COLOR 用于提取颜色对字段信息的位掩码

键由名称以 KEY_ 开头的整数常量引用。确切的可用键取决于系统。

关键常数
KEY_MIN 最小键值
KEY_BREAK 中断键(不可靠)
KEY_DOWN 向下箭头
KEY_UP 向上箭头
KEY_LEFT 向左箭头
KEY_RIGHT 向右箭头
KEY_HOME Home 键 (上+左箭头)
KEY_BACKSPACE 退格(不可靠)
KEY_F0 功能键。 支持至多 64 个功能键。
KEY_Fn 功能键 n 的值
KEY_DL 删除行
KEY_IL 插入行
KEY_DC 删除字符
KEY_IC 插入字符或进入插入模式
KEY_EIC 退出插入字符模式
KEY_CLEAR 清空屏幕
KEY_EOS 清空至屏幕底部
KEY_EOL 清空至行尾
KEY_SF 向前滚动 1 行
KEY_SR 向后滚动 1 行 (反转)
KEY_NPAGE 下一页
KEY_PPAGE 上一页
KEY_STAB 设置制表符
KEY_CTAB 清除制表符
KEY_CATAB 清除所有制表符
KEY_ENTER 回车或发送 (不可靠)
KEY_SRESET 软 (部分) 重置 (不可靠)
KEY_RESET 重置或硬重置 (不可靠)
KEY_PRINT 打印
KEY_LL Home 向下或到底 (左下)
KEY_A1 键盘的左上角
KEY_A3 键盘的右上角
KEY_B2 键盘的中心
KEY_C1 键盘左下方
KEY_C3 键盘右下方
KEY_BTAB 回退制表符
KEY_BEG Beg (开始)
KEY_CANCEL 取消
KEY_CLOSE 关闭
KEY_COMMAND Cmd (命令行)
KEY_COPY 复制
KEY_CREATE 创建
KEY_END End
KEY_EXIT 退出
KEY_FIND 查找
KEY_HELP 帮助
KEY_MARK 标记
KEY_MESSAGE 消息
KEY_MOVE 移动
KEY_NEXT 下一个
KEY_OPEN 打开
KEY_OPTIONS 选项
KEY_PREVIOUS Prev (上一个)
KEY_REDO 重做
KEY_REFERENCE Ref (引用)
KEY_REFRESH 刷新
KEY_REPLACE 替换
KEY_RESTART 重启
KEY_RESUME 恢复
KEY_SAVE 保存
KEY_SBEG Shift + Beg (开始)
KEY_SCANCEL Shift + Cancel
KEY_SCOMMAND Shift + Command
KEY_SCOPY Shift + Copy
KEY_SCREATE Shift + Create
KEY_SDC Shift + 删除字符
KEY_SDL Shift + 删除行
KEY_SELECT 选择
KEY_SEND Shift + End
KEY_SEOL Shift + 清空行
KEY_SEXIT Shift + 退出
KEY_SFIND Shift + 查找
KEY_SHELP Shift + 帮助
KEY_SHOME Shift + Home
KEY_SIC Shift + 输入
KEY_SLEFT Shift + 向左箭头
KEY_SMESSAGE Shift + 消息
KEY_SMOVE Shift + 移动
KEY_SNEXT Shift + 下一个
KEY_SOPTIONS Shift + 选项
KEY_SPREVIOUS Shift + 上一个
KEY_SPRINT Shift + 打印
KEY_SREDO Shift + 重做
KEY_SREPLACE Shift + 替换
KEY_SRIGHT Shift + 向右箭头
KEY_SRSUME Shift + 恢复
KEY_SSAVE Shift + 保存
KEY_SSUSPEND Shift + 挂起
KEY_SUNDO Shift + 撤销
KEY_SUSPEND 挂起
KEY_UNDO 撤销操作
KEY_MOUSE 鼠标事件已发生
KEY_RESIZE 终端大小改变事件
KEY_MAX 最大键值

在VT100及其软件仿真(例如X终端仿真器)上,通常至少有四个功能键( KEY_F1, KEY_F2, KEY_F3, KEY_F4 )可用,并且箭头键以明显的方式映射到 KEY_UP, KEY_DOWN, KEY_LEFTKEY_RIGHT 。如果您的机器有一个PC键盘,可以安全地使用箭头键和十二个功能键(旧的PC键盘可能只有十个功能键);此外,以下键盘映射是标准的:

键帽 常量
Insert KEY_IC
Delete KEY_DC
Home KEY_HOME
End KEY_END
Page Up KEY_PPAGE
Page Down KEY_NPAGE

下表列出了替代字符集中的字符。 这些字符继承自 VT100 终端,在 X 终端等软件模拟器上通常均为可用。 当没有可用的图形时,curses 会回退为粗糙的可打印 ASCII 近似符号。

注解

只有在调用 initscr() 之后才能使用它们

ACS代码 含意
ACS_BBSS 右上角的别名
ACS_BLOCK 实心方块
ACS_BOARD 正方形
ACS_BSBS 水平线的别名
ACS_BSSB 左上角的别名
ACS_BSSS 顶部 T 型的别名
ACS_BTEE 底部 T 型
ACS_BULLET 正方形
ACS_CKBOARD 棋盘(点刻)
ACS_DARROW 向下箭头
ACS_DEGREE 等级符
ACS_DIAMOND 菱形
ACS_GEQUAL 大于或等于
ACS_HLINE 水平线
ACS_LANTERN 灯形符号
ACS_LARROW 向左箭头
ACS_LEQUAL 小于或等于
ACS_LLCORNER 左下角
ACS_LRCORNER 右下角
ACS_LTEE 左侧 T 型
ACS_NEQUAL 不等号
ACS_PI 字母π
ACS_PLMINUS 正负号
ACS_PLUS 加号
ACS_RARROW 向右箭头
ACS_RTEE 右侧 T 型
ACS_S1 扫描线 1
ACS_S3 扫描线3
ACS_S7 扫描线7
ACS_S9 扫描线 9
ACS_SBBS 右下角的别名
ACS_SBSB 垂直线的别名
ACS_SBSS 右侧 T 型的别名
ACS_SSBB 左下角的别名
ACS_SSBS 底部 T 型的别名
ACS_SSSB 左侧 T 型的别名
ACS_SSSS 交叉或大加号的替代名称
ACS_STERLING 英镑
ACS_TTEE 顶部 T 型
ACS_UARROW 向上箭头
ACS_ULCORNER 左上角
ACS_URCORNER 右上角
ACS_VLINE 垂线

下表列出了预定义的颜色:

常量 颜色
COLOR_BLACK 黑色
COLOR_BLUE 蓝色
COLOR_CYAN 青色(浅绿蓝色)
COLOR_GREEN 绿色
COLOR_MAGENTA 洋红色(紫红色)
COLOR_RED 红色
COLOR_WHITE 白色
COLOR_YELLOW 黄色

curses.textpad —- 用于 curses 程序的文本输入控件

curses.textpad 模块提供了一个 Textbox 类,该类在 curses 窗口中处理基本的文本编辑,支持一组与 Emacs 类似的键绑定(因此这也适用于 Netscape Navigator, BBedit 6.x, FrameMaker 和许多其他程序)。 该模块还提供了一个绘制矩形的函数,适用于容纳文本框或其他目的。

curses.textpad 模块定义了以下函数:

curses.textpad.rectangle(win, uly, ulx, lry, lrx)

绘制一个矩形。 第一个参数必须为窗口对象;其余参数均为相对于该窗口的坐标值。 第二和第三个参数为要绘制的矩形的左上角的 y 和 x 坐标值;第四和第五个参数为其右下角的 y 和 x 坐标值。 将会使用 VT100/IBM PC 形式的字符在可用的终端上(包括 xterm 和大多数其他软件终端模拟器)绘制矩形。 在其他情况下则将使用 ASCII 横杠、竖线和加号绘制。

文本框对象

你可以通过如下方式实例化一个 Textbox:

class curses.textpad.Textbox(win)

返回一个文本框控件对象。 win 参数必须是一个 curses 窗口 对象,文本框将被包含在其中。 文本框的编辑光标在初始时位于包含窗口的左上角,坐标值为 (0, 0)。 实例的 stripspaces 旗标初始时为启用。

Textbox 对象具有以下方法:

  • edit([validator])

    这是你通常将使用的入口点。 它接受编辑按键直到键入了一个终止按键。 如果提供了 validator,它必须是一个函数。 它将在每次按键时被调用并传入相应的按键作作为形参;命令发送将在结果上执行。 此方法会以字符串形式返回窗口内容;是否包括窗口中的空白将受到 stripspaces 属性的影响。

  • do_command(ch)

    处理单个按键命令。以下是支持的特殊按键:

    按键 动作
    Control-A 转到窗口的左边缘。
    Control-B 光标向左,如果可能,包含前一行。
    Control-D 删除光标下的字符。
    Control-E 前往右边缘(stripspaces 关闭时)或者行尾(stripspaces 启用时)。
    Control-F 向右移动光标,适当时换行到下一行。
    Control-G 终止,返回窗口内容。
    Control-H 向后删除字符。
    Control-J 如果窗口是1行则终止,否则插入换行符。
    Control-K 如果行为空,则删除它,否则清除到行尾。
    Control-L 刷新屏幕。
    Control-N 光标向下;向下移动一行。
    Control-O 在光标位置插入一个空行。
    Control-P 光标向上;向上移动一行。

    如果光标位于无法移动的边缘,则移动操作不执行任何操作。在可能的情况下,支持以下同义词:

    常量 按键
    KEY_LEFT Control-B
    KEY_RIGHT Control-F
    KEY_UP Control-P
    KEY_DOWN Control-N
    KEY_BACKSPACE Control-h

    所有其他按键将被视为插入给定字符并右移的命令(带有自动折行)。

  • gather()

    以字符串形式返回窗口内容;是否包括窗口中的空白将受到 stripspaces 成员的影响。

  • stripspaces

    此属性是控制窗口中空白解读方式的旗标。 当启用时,每一行的末尾空白会被忽略;任何将光标定位至末尾空白的光标动作都将改为前往该行末尾,并且在收集窗口内容时将去除末尾空白。

curses.ascii —- 用于 ASCII 字符的工具

curses.ascii 模块提供了一些 ASCII 字符的名称常量以及在各种 ASCII 字符类中执行成员检测的函数。 所提供的控制字符常量如下:

名称 含意
NUL
SOH 标题开始,控制台中断
STX 文本开始
ETX 文本结束
EOT 传输结束
ENQ 查询,附带 ACK 流量控制
ACK 确认
BEL 蜂鸣器
BS 退格
TAB 制表符
HT TAB 的别名: “水平制表符”
LF 换行
NL LF 的别名: “新行”
VT 垂直制表符
FF 换页
CR 回车
SO Shift-out,开始替换字符集
SI Shift-in,恢复默认字符集
DLE Data-link escape,数据链接转义
DC1 XON,用于流程控制
DC2 Device control 2,块模式流程控制
DC3 XOFF,用于流程控制
DC4 设备控制4
NAK 否定确认
SYN 同步空闲
ETB 末端传输块
CAN 取消
EM 媒体结束
SUB 替换
ESC 退出
FS 文件分隔符
GS 组分隔符
RS Record separator,块模式终止符
US 单位分隔符
SP 空格
DEL 删除

请注意其中有许多在现今已经没有实际作用。 这些助记符是来源于数字计算机之前的电传打印机规范。

此模块提供了下列函数,对应于标准 C 库中的函数:

curses.ascii.isalnum(c)

检测 ASCII 字母数字类字符;它等价于 isalpha(c) 或 isdigit(c)

curses.ascii.isalpha(c)

检测 ASCII 字母类字符;它等价于 isupper(c) or islower(c)

curses.ascii.isascii(c)

检测字符值是否在 7 位 ASCII 集范围内。

curses.ascii.isblank(c)

检测 ASCII 空白字符;包括空格或水平制表符。

curses.ascii.iscntrl(c)

检测 ASCII 控制字符(在 0x00 到 0x1f 或 0x7f 范围内)。

curses.ascii.isdigit(c)

检测 ASCII 十进制数码,即 '0''9'。 它等价于 c in string.digits

curses.ascii.isgraph(c)

检测任意 ASCII 可打印字符,不包括空白符。

curses.ascii.islower(c)

检测 ASCII 小写字母字符。

curses.ascii.isprint(c)

检测任意 ASCII 可打印字符,包括空白符。

curses.ascii.ispunct(c)

检测任意 ASCII 可打印字符,不包括空白符或字母数字类字符。

curses.ascii.isspace(c)

检测 ASCII 空白字符;包括空格,换行,回车,进纸,水平制表和垂直制表。

curses.ascii.isupper(c)

检测 ASCII 大写字母字符。

curses.ascii.isxdigit(c)

检测 ASCII 十六进制数码。 这等价于 c in string.hexdigits

curses.ascii.isctrl(c)

检测 ASCII 控制字符(码位值 0 至 31)。

curses.ascii.ismeta(c)

检测非 ASCII 字符(码位值 0x80 及以上)。

这些函数接受整数或单字符字符串;当参数为字符串时,会先使用内置函数 ord() 进行转换。

请注意所有这些函数都是检测根据你传入的字符串的字符所生成的码位值;它们实际上完全不会知晓本机的字符编码格式。

以下两个函数接受单字符字符串或整数形式的字节值;它们会返回相同类型的值。

curses.ascii.ascii(c)

返回对应于 c 的下个 7 比特位的 ASCII 值。

curses.ascii.ctrl(c)

返回对应于给定字符的控制字符(字符比特值会与 0x1f 进行按位与运算)。

curses.ascii.alt(c)

返回对应于给定 ASCII 字符的 8 比特位字符(字符比特值会与 0x80 进行按位或运算)。

以下函数接受单字符字符串或整数值;它会返回一个字符串。

curses.ascii.unctrl(c)

返回 ASCII 字符 c 的字符串表示形式。 如果 c 是可打印字符,则字符串为字符本身。 如果该字符是控制字符 (0x00—0x1f) 则字符串由一个插入符 ('^') 加相应的大写字母组成。 如果该字符是 ASCII 删除符 (0x7f) 则字符串为 '^?'。 如果该字符设置了元比特位 (0x80),元比特位会被去除,应用以上规则后将在结果之前添加 '!'

curses.ascii.controlnames

一个 33 元素的字符串数据,其中按从 0 (NUL) 到 0x1f (US) 的顺序包含了三十二个 ASCII 控制字符的 ASCII 助记符,另加空格符的助记符 SP

curses.panel —- curses 的面板栈扩展

面板是具有添加深度功能的窗口,因此它们可以从上至下堆叠为栈,只有显示每个窗口的可见部分会显示出来。 面板可以在栈中被添加、上移或下移,也可以被移除。

函数

curses.panel 模块定义了以下函数:

curses.panel.bottom_panel()

返回面板栈中的底部面板。

curses.panel.new_panel(win)

返回一个面板对象,将其与给定的窗口 win 相关联。 请注意你必须显式地保持所返回的面板对象。 如果你不这样做,面板对象会被垃圾回收并从面板栈中被移除。

curses.panel.top_panel()

返回面板栈中的顶部面板。

curses.panel.update_panels()

在面板栈发生改变后更新虚拟屏幕。 这不会调用 curses.doupdate(),因此你不必自己执行此操作。

Panel 对象

Panel 对象,如上面 new_panel() 所返回的对象,是带有栈顺序的多个窗口。 总是会有一个窗口与确定内容的面板相关联,面板方法会负责窗口在面板栈中的深度。

Panel 对象具有以下方法:

Panel.above()

返回当前面板之上的面板。

Panel.below()

返回当前面板之下的面板。

Panel.bottom()

将面板推至栈底部。

Panel.hidden()

如果面板被隐藏(不可见)则返回 True,否则返回 False

Panel.hide()

隐藏面板。 这不会删除对象,它只是让窗口在屏幕上不可见。

Panel.move(y, x)

将面板移至屏幕坐标 (y, x)

Panel.replace(win)

将与面板相关联的窗口改为窗口 win

Panel.set_userptr(obj)

将面板的用户指向设为 obj。 这被用来将任意数据与面板相关联,数据可以是任何 Python 对象。

Panel.show()

显示面板(面板可能已被隐藏)。

Panel.top()

将面板推至栈顶部。

Panel.userptr()

返回面板的用户指针。 这可以是任何 Python 对象。

Panel.window()

返回与面板相关联的窗口对象。

platform —- 获取底层平台的标识数据

源代码: Lib/platform.py


注解

特定平台按字母顺序排列,Linux 包括在 Unix 小节之中。

跨平台

platform.architecture(executable=sys.executable, bits=’’, linkage=’’)

查询给定的可执行文件(默认为 Python 解释器二进制码文件)来获取各种架构信息。

返回一个元素 (bits, linkage),其中包含可执行文件所使用的位架构和链接格式信息。 这两个值均以字符串形式返回。

无法确定的值将返回为形参预设所给出的值。 如果给出的位数为 '',则会使用 sizeof(pointer) (或者当 Python 版本 < 1.5.2 时为 sizeof(long)) 作为所支持的指针大小的提示。

此函数依赖于系统的 file 命令来执行实际的操作。 这在几乎所有 Unix 平台和某些非 Unix 平台上只有当可执行文件指向 Python 解释器时才可用。 当以上要求不满足时将会使用合理的默认值。

注解

在 macOS (也许还有其他平台) 上,可执行文件可能是包含多种架构的通用文件。

要获取当前解释器的“64 位性”,更可靠的做法是查询 sys.maxsize 属性:

is_64bits = sys.maxsize > 2**32

platform.machine()

返回机器类型,例如 'i386'。 如果该值无法确定则会返回一个空字符串。

platform.node()

返回计算机的网络名称(可能不是完整限定名称!)。 如果该值无法确定则会返回一个空字符串。

platform.platform(aliased=0, terse=0)

返回一个标识底层平台的字符串,其中带有尽可能多的有用信息。

输出信息的目标是“人类易读”而非机器易解析。 它在不同平台上可能看起来不一致,这是有意为之的。

如果 aliased 为真值,此函数将使用各种平台不同与其通常名称的别名来报告系统名称,例如 SunOS 将被报告为 Solaris。 system_alias() 函数将被用于实现此功能。

terse 设为真值将导致此函数只返回标识平台所必须的最小量信息。

在 3.8 版更改: 在 macOS 上,此函数现在会在 mac_ver() 返回的发布版字符串非空时使用它,以便获取 macOS 版本而非 darwin 版本。

platform.processor()

返回(真实的)处理器名称,例如 'amdk6'

如果该值无法确定则将返回空字符串。 请注意许多平台都不提供此信息或是简单地返回与 machine() 相同的值。 NetBSD 则会提供此信息。

platform.python_build()

返回一个元组 (buildno, builddate),以字符串表示的 Python 编译代码和日期。

platform.python_compiler()

返回一个标识用于编译 Python 的编译器的的字符串。

platform.python_branch()

返回一个标识 Python 实现的 SCM 分支的字符串。

platform.python_implementation()

返回一个标识 Python 实现的字符串。 可能的返回值有: ‘CPython’, ‘IronPython’, ‘Jython’, ‘PyPy’。

platform.python_revision()

返回一个标识 Python 实现的 SCM 修订版的字符串。

platform.python_version()

将 Python 版本以字符串 'major.minor.patchlevel' 形式返回。

请注意此返回值不同于 Python sys.version,它将总是包括 patchlevel (默认为 0)。

platform.python_version_tuple()

将 Python 版本以字符串元组 (major, minor, patchlevel) 形式返回。

请注意此返回值不同于 Python sys.version,它将总是包括 patchlevel (默认为 '0')。

platform.release()

返回系统的发布版本,例如 '2.2.0''NT',如果该值无法确定则将返回一个空字符串。

platform.system()

返回系统平台/OS的名称,例如 'Linux', 'Darwin', 'Java', 'Windows'。 如果该值无法确定则将返回一个空字符串。

platform.system_alias(system, release, version)

返回别名为某些系统所使用的常见营销名称的 (system, release, version)。 它还会在可能导致混淆的情况下对信息进行一些重排序操作。

platform.version()

返回系统的发布版本信息,例如 '#3 on degas'。 如果该值无法确定则将返回一个空字符串。

platform.uname()

具有高可移植性的 uname 接口。 返回包含六个属性的 namedtuple(): system, node, release, version, machineprocessor

请注意此函数添加的第六个属性 (processor) 并不存在于 os.uname() 的结果中。 并且前两个属性的属性名称也不一致;os.uname() 是将它们称为 sysnamenodename

无法确定的条目会被设为 ''

在 3.3 版更改: 将结果从元组改为命名元组。

Java平台

platform.java_ver(release=’’, vendor=’’, vminfo=’’, ‘’, ‘’, osinfo=’’, ‘’, ‘’)

Jython 的版本接口

返回一个元组 (release, vendor, vminfo, osinfo),其中 vminfo 为元组 (vm_name, vm_release, vm_vendor)osinfo 为元组 (os_name, os_version, os_arch)。 无法确定的值将设为由形参所给出的默认值 (默认均为 '')。

Windows平台

platform.win32_ver(release=’’, version=’’, csd=’’, ptype=’’)

从 Windows 注册表获取额外的版本信息并返回一个元组 (release, version, csd, ptype) 表示 OS 发行版, 版本号, CSD 级别 (Service Pack) 和 OS 类型 (多个/单个处理器)。

一点提示: ptype 在单个处理器的 NT 机器上为 'Uniprocessor Free' 而在多个处理器的机器上为 'Multiprocessor Free'‘Free’ 是指该 OS 版本没有调试代码。 它还可能显示 ‘Checked’ 表示该 OS 版本使用了调试代码,即检测参数、范围等的代码。

platform.win32_edition()

返回一个代表当前 Windows 版本的字符串。 可能的值包括但不限于 'Enterprise', 'IoTUAP', 'ServerStandard''nanoserver'

3.8 新版功能.

platform.win32_is_iot()

如果 win32_edition() 返回的 Windows 版本被识别为 IoT 版则返回 True

3.8 新版功能.

macOS Platform

platform.mac_ver(release=’’, versioninfo=’’, ‘’, ‘’, machine=’’)

Get macOS version information and return it as tuple (release, versioninfo, machine) with versioninfo being a tuple (version, dev_stage, non_release_version).

无法确定的条目会被设为 ''。 所有元组条目均为字符串。

Unix 平台

platform.libc_ver(executable=sys.executable, lib=’’, version=’’, chunksize=16384)

尝试确定可执行文件(默认为 Python 解释器)所链接到的 libc 版本。 返回一个字符串元组 (lib, version),当查找失败时其默认值将设为给定的形参值。

请注意此函数对于不同 libc 版本向可执行文件添加符号的方式有深层的关联,可能仅适用于使用 gcc 编译出来的可执行文件。

文件将按 chunksize 个字节的分块来读取和扫描。

Linux 平台

platform.freedesktop_os_release()

os-release 文件获取操作系统标识并将其作为一个字典返回。 os-release 文件是 freedesktop.org 标准 并在大多数 Linux 发行版上可用。 一个重要的例外是 Android 和基于 Android 的发行版。

/etc/os-release/usr/lib/os-release 均无法读取时将引发 OSError 或其子类。

成功时,该函数将返回一个字典,其中键和值均为字符串。 值当中的特殊字符例如 "{TX-PL-LABEL}#x60; 会被复原。 字段 ``NAME,IDPRETTY_NAME` 总是会按照标准来定义。 所有其他字段都是可选的。 厂商可能会包括额外的字段。

请注意 NAME, VERSIONVARIANT 等字段是适用于向用户展示的字符串。 程序应当使用 ID, ID_LIKE, VERSION_IDVARIANT_ID 等字段来标识 Linux 发行版。

示例:

def get_like_distro():
    info = platform.freedesktop_os_release()
    ids = [info["ID"]]
    if "ID_LIKE" in info:
        # ids are space separated and ordered by precedence
        ids.extend(info["ID_LIKE"].split())
    return ids

errno —- 标准 errno 系统符号

本模块提供标准的 errno 系统符号。每个符号的值是其对应的整数值。符号的名称和描述来自 linux/include/errno.h,应该是非常全面的。

errno.errorcode

提供从 errno 值到底层系统中字符串名称的映射的字典。例如, errno.errorcode[errno.EPERM] 映射为 'EPERM'

如果要将数字的错误代码转换为错误信息,请使用 os.strerror()

在下面的列表中,当前平台上没有使用的符号没有被本模块定义。 已定义的符号的具体列表可参见 errno.errorcode.keys()。 可用的符号包括:

errno.EPERM

操作不被允许

errno.ENOENT

无此文件或目录

errno.ESRCH

无此进程

errno.EINTR

系统调用中断。

参见

此错误被映射到异常 InterruptedError

errno.EIO

I/O 错误

errno.ENXIO

无此设备或地址

errno.E2BIG

参数列表过长

errno.ENOEXEC

执行格式错误

errno.EBADF

错误的文件号

errno.ECHILD

没有子进程

errno.EAGAIN

重试

errno.ENOMEM

内存不足

errno.EACCES

没有权限

errno.EFAULT

错误的地址

errno.ENOTBLK

需要块设备

errno.EBUSY

设备或资源忙

errno.EEXIST

文件已存在

errno.EXDEV

跨设备链接

errno.ENODEV

无此设备

errno.ENOTDIR

不是目录

errno.EISDIR

是目录

errno.EINVAL

无效的参数

errno.ENFILE

文件表溢出

errno.EMFILE

打开的文件过多

errno.ENOTTY

不是打字机

errno.ETXTBSY

文本文件忙

errno.EFBIG

文件过大

errno.ENOSPC

设备已无可用空间

errno.ESPIPE

非法查找

errno.EROFS

只读文件系统

errno.EMLINK

链接过多

errno.EPIPE

管道已损坏

errno.EDOM

数学参数超出函数范围

errno.ERANGE

数学运算结果无法表示

errno.EDEADLK

将发生资源死锁

errno.ENAMETOOLONG

文件名过长

errno.ENOLCK

没有可用的记录锁

errno.ENOSYS

功能未实现

errno.ENOTEMPTY

目录非空

errno.ELOOP

遇到过多的符号链接

errno.EWOULDBLOCK

操作将阻塞

errno.ENOMSG

没有所需类型的消息

errno.EIDRM

标识符被移除

errno.ECHRNG

信道编号超出范围

errno.EL2NSYNC

级别 2 未同步

errno.EL3HLT

级别 3 已停止

errno.EL3RST

级别 3 重置

errno.ELNRNG

链接编号超出范围

errno.EUNATCH

未附加协议驱动

errno.ENOCSI

没有可用的 CSI 结构

errno.EL2HLT

级别 2 已停止

errno.EBADE

无效的交换

errno.EBADR

无效的请求描述符

errno.EXFULL

交换已满

errno.ENOANO

没有阳极

errno.EBADRQC

无效的请求码·

errno.EBADSLT

无效的槽位

errno.EDEADLOCK

文件锁定死锁错误

errno.EBFONT

错误的字体文件格式

errno.ENOSTR

设备不是流

errno.ENODATA

没有可用的数据

errno.ETIME

计时器已到期

errno.ENOSR

流资源不足

errno.ENONET

机器不在网络上

errno.ENOPKG

包未安装

errno.EREMOTE

对象是远程的

errno.ENOLINK

链接已被切断

errno.EADV

广告错误

errno.ESRMNT

挂载错误

errno.ECOMM

发送时通讯错误

errno.EPROTO

协议错误

errno.EMULTIHOP

已尝试多跳

errno.EDOTDOT

RFS 专属错误

errno.EBADMSG

非数据消息

errno.EOVERFLOW

值相对于已定义数据类型过大

errno.ENOTUNIQ

名称在网络上不唯一

errno.EBADFD

文件描述符处于错误状态

errno.EREMCHG

远端地址已改变

errno.ELIBACC

无法访问所需的共享库

errno.ELIBBAD

访问已损坏的共享库

errno.ELIBSCN

a.out 中的 .lib 部分已损坏

errno.ELIBMAX

尝试链接过多的共享库

errno.ELIBEXEC

无法直接执行共享库

errno.EILSEQ

非法字节序列

errno.ERESTART

已中断系统调用需要重启

errno.ESTRPIPE

流管道错误

errno.EUSERS

用户过多

errno.ENOTSOCK

在非套接字上执行套接字操作

errno.EDESTADDRREQ

需要目标地址

errno.EMSGSIZE

消息过长

errno.EPROTOTYPE

套接字的协议类型错误

errno.ENOPROTOOPT

协议不可用

errno.EPROTONOSUPPORT

协议不受支持

errno.ESOCKTNOSUPPORT

套接字类型不受支持

errno.EOPNOTSUPP

操作在传输端点上不受支持

errno.EPFNOSUPPORT

协议族不受支持

errno.EAFNOSUPPORT

地址族不受协议支持

errno.EADDRINUSE

地址已被使用

errno.EADDRNOTAVAIL

无法分配要求的地址

errno.ENETDOWN

网络已断开

errno.ENETUNREACH

网络不可达

errno.ENETRESET

网络因重置而断开连接

errno.ECONNABORTED

软件导致连接中止

errno.ECONNRESET

连接被对方重置

errno.ENOBUFS

没有可用的缓冲区空间

errno.EISCONN

传输端点已连接

errno.ENOTCONN

传输端点未连接

errno.ESHUTDOWN

传输端点关闭后无法发送

errno.ETOOMANYREFS

引用过多:无法拼接

errno.ETIMEDOUT

连接超时

errno.ECONNREFUSED

连接被拒

errno.EHOSTDOWN

主机已关闭

errno.EHOSTUNREACH

没有到主机的路由

errno.EALREADY

操作已在进行

errno.EINPROGRESS

操作正在进行

errno.ESTALE

过期的 NFS 文件句柄

errno.EUCLEAN

结构需要清理

errno.ENOTNAM

不是 XENIX 命名类型文件

errno.ENAVAIL

没有可用的 XENIX 信标

errno.EISNAM

是命名类型文件

errno.EREMOTEIO

远程 I/O 错误

errno.EDQUOT

超出配额

ctypes —- Python 的外部函数库

ctypes 是 Python 的外部函数库。它提供了与 C 兼容的数据类型,并允许调用 DLL 或共享库中的函数。可使用该模块以纯 Python 形式对这些库进行封装。

ctypes 教程

Note: The code samples in this tutorial use doctest to make sure that they actually work. Since some code samples behave differently under Linux, Windows, or macOS, they contain doctest directives in comments.

注意:部分示例代码引用了 ctypes c_int 类型。在 sizeof(long) == sizeof(int) 的平台上此类型是 c_long 的一个别名。所以,在程序输出 c_long 而不是你期望的 c_int 时不必感到迷惑 —- 它们实际上是同一种类型。

载入动态连接库

ctypes 导出了 cdll 对象,在 Windows 系统中还导出了 windlloledll 对象用于载入动态连接库。

通过操作这些对象的属性,你可以载入外部的动态链接库。cdll 载入按标准的 cdecl 调用协议导出的函数,而 windll 导入的库按 stdcall 调用协议调用其中的函数。 oledll 也按 stdcall 调用协议调用其中的函数,并假定该函数返回的是 Windows HRESULT 错误代码,并当函数调用失败时,自动根据该代码甩出一个 OSError 异常。

在 3.3 版更改: 原来在 Windows 下抛出的异常类型 WindowsError 现在是 OSError 的一个别名。

这是一些 Windows 下的例子。注意:msvcrt 是微软 C 标准库,包含了大部分 C 标准函数,这些函数都是以 cdecl 调用协议进行调用的。

>>> from ctypes import *
>>> print(windll.kernel32)  
<WinDLL 'kernel32', handle ... at ...>
>>> print(cdll.msvcrt)      
<CDLL 'msvcrt', handle ... at ...>
>>> libc = cdll.msvcrt      
>>>

Windows 会自动添加通常的 .dll 文件扩展名。

注解

通过 cdll.msvcrt 调用的标准 C 函数,可能会导致调用一个过时的,与当前 Python 所不兼容的函数。因此,请尽量使用标准的 Python 函数,而不要使用 msvcrt 模块。

在 Linux 下,必须使用 包含 文件扩展名的文件名来导入共享库。因此不能简单使用对象属性的方式来导入库。因此,你可以使用方法 LoadLibrary(),或构造 CDLL 对象来导入库。

>>> cdll.LoadLibrary("libc.so.6")  
<CDLL 'libc.so.6', handle ... at ...>
>>> libc = CDLL("libc.so.6")       
>>> libc                           
<CDLL 'libc.so.6', handle ... at ...>
>>>

操作导入的动态链接库中的函数

通过操作dll对象的属性来操作这些函数。

>>> from ctypes import *
>>> libc.printf
<_FuncPtr object at 0x...>
>>> print(windll.kernel32.GetModuleHandleA)  
<_FuncPtr object at 0x...>
>>> print(windll.kernel32.MyOwnFunction)     
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "ctypes.py", line 239, in __getattr__
    func = _StdcallFuncPtr(name, self)
AttributeError: function 'MyOwnFunction' not found
>>>

注意:Win32 系统的动态库,比如 kernel32user32,通常会同时导出同一个函数的 ANSI 版本和 UNICODE 版本。UNICODE 版本通常会在名字最后以 W 结尾,而 ANSI 版本的则以 A 结尾。 win32的 GetModuleHandle 函数会根据一个模块名返回一个 模块句柄,该函数暨同时包含这样的两个版本的原型函数,并通过宏 UNICODE 是否定义,来决定宏 GetModuleHandle 导出的是哪个具体函数。

/* ANSI version */
HMODULE GetModuleHandleA(LPCSTR lpModuleName);
/* UNICODE version */
HMODULE GetModuleHandleW(LPCWSTR lpModuleName);

windll 不会通过这样的魔法手段来帮你决定选择哪一种函数,你必须显式的调用 GetModuleHandleAGetModuleHandleW,并分别使用字节对象或字符串对象作参数。

有时候,dlls的导出的函数名不符合 Python 的标识符规范,比如 "??2@YAPAXI@Z"。此时,你必须使用 getattr() 方法来获得该函数。

>>> getattr(cdll.msvcrt, "??2@YAPAXI@Z")  
<_FuncPtr object at 0x...>
>>>

Windows 下,有些 dll 导出的函数没有函数名,而是通过其顺序号调用。对此类函数,你也可以通过 dll 对象的数值索引来操作这些函数。

>>> cdll.kernel32[1]  
<_FuncPtr object at 0x...>
>>> cdll.kernel32[0]  
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "ctypes.py", line 310, in __getitem__
    func = _StdcallFuncPtr(name, self)
AttributeError: function ordinal 0 not found
>>>

调用函数

你可以貌似是调用其它 Python 函数那样直接调用这些函数。在这个例子中,我们调用了 time() 函数,该函数返回一个系统时间戳(从 Unix 时间起点到现在的秒数),而GetModuleHandleA() 函数返回一个 win32 模块句柄。

此函数中调用的两个函数都使用了空指针(用 None 作为空指针):

>>> print(libc.time(None))  
1150640792
>>> print(hex(windll.kernel32.GetModuleHandleA(None)))  
0x1d000000
>>>

如果你用 cdecl 调用方式调用 stdcall 约定的函数,则会甩出一个异常 ValueError。反之亦然。

>>> cdll.kernel32.GetModuleHandleA(None)  
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: Procedure probably called with not enough arguments (4 bytes missing)
>>>
>>> windll.msvcrt.printf(b"spam")  
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: Procedure probably called with too many arguments (4 bytes in excess)
>>>

你必须阅读这些库的头文件或说明文档来确定它们的正确的调用协议。

在 Windows 中,ctypes 使用 win32 结构化异常处理来防止由于在调用函数时使用非法参数导致的程序崩溃。

>>> windll.kernel32.GetModuleHandleA(32)  
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OSError: exception: access violation reading 0x00000020
>>>

然而,总有许多办法,通过调用 ctypes 使得 Python 程序崩溃。因此,你必须小心使用。 faulthandler 模块可以用于帮助诊断程序崩溃的原因。(比如由于错误的C库函数调用导致的段错误)。

None,整型,字节对象和(UNICODE)字符串是仅有的可以直接作为函数参数使用的四种Python本地数据类型。None作为C的空指针 (NULL`),字节和字符串类型作为一个指向其保存数据的内存块指针 (char* 或 wchar_t*)。Python 的整型则作为平台默认的C的 int 类型,他们的数值被截断以适应C类型的整型长度。

在我们开始调用函数前,我们必须先了解作为函数参数的 ctypes 数据类型。

基础数据类型

ctypes 定义了一些和C兼容的基本数据类型:

ctypes 类型 C 类型 Python 类型
c_bool _Bool bool (1)
c_char char 单字符字节串对象
c_wchar wchar_t 单字符字符串
c_byte char int
c_ubyte unsigned char int
c_short short int
c_ushort unsigned short int
c_int int int
c_uint unsigned int int
c_long long int
c_ulong unsigned long int
c_longlong int64 或 long long int
c_ulonglong unsigned int64 或 unsigned long long int
c_size_t size_t int
c_ssize_t ssize_tPy_ssize_t int
c_float float float
c_double double float
c_longdouble long double float
c_char_p char (以 NUL 结尾) 字节串对象或 None
c_wchar_p wchar_t (以 NUL 结尾) 字符串或 None
c_void_p void* int 或 None
  1. 构造函数接受任何具有真值的对象。

所有这些类型都可以通过使用正确类型和值的可选初始值调用它们来创建:

>>> c_int()
c_long(0)
>>> c_wchar_p("Hello, World")
c_wchar_p(140018365411392)
>>> c_ushort(-3)
c_ushort(65533)
>>>

由于这些类型是可变的,它们的值也可以在以后更改:

>>> i = c_int(42)
>>> print(i)
c_long(42)
>>> print(i.value)
42
>>> i.value = -99
>>> print(i.value)
-99
>>>

当给指针类型的对象 c_char_p, c_wchar_pc_void_p 等赋值时,将改变它们所指向的 内存地址*,而 *不是 它们所指向的内存区域的 内容 (这是理所当然的,因为 Python 的 bytes 对象是不可变的):

>>> s = "Hello, World"
>>> c_s = c_wchar_p(s)
>>> print(c_s)
c_wchar_p(139966785747344)
>>> print(c_s.value)
Hello World
>>> c_s.value = "Hi, there"
>>> print(c_s)              # the memory location has changed
c_wchar_p(139966783348904)
>>> print(c_s.value)
Hi, there
>>> print(s)                # first object is unchanged
Hello, World
>>>

但你要注意不能将它们传递给会改变指针所指内存的函数。如果你需要可改变的内存块,ctypes 提供了 create_string_buffer() 函数,它提供多种方式创建这种内存块。当前的内存块内容可以通过 raw 属性存取,如果你希望将它作为NUL结束的字符串,请使用 value 属性:

>>> from ctypes import *
>>> p = create_string_buffer(3)            # create a 3 byte buffer, initialized to NUL bytes
>>> print(sizeof(p), repr(p.raw))
3 b'\x00\x00\x00'
>>> p = create_string_buffer(b"Hello")     # create a buffer containing a NUL terminated string
>>> print(sizeof(p), repr(p.raw))
6 b'Hello\x00'
>>> print(repr(p.value))
b'Hello'
>>> p = create_string_buffer(b"Hello", 10) # create a 10 byte buffer
>>> print(sizeof(p), repr(p.raw))
10 b'Hello\x00\x00\x00\x00\x00'
>>> p.value = b"Hi"
>>> print(sizeof(p), repr(p.raw))
10 b'Hi\x00lo\x00\x00\x00\x00\x00'
>>>

create_string_buffer() 函数替代以前的ctypes版本中的 c_buffer() 函数 (仍然可当作别名使用)和 c_string() 函数。create_unicode_buffer() 函数创建包含 unicode 字符的可变内存块,与之对应的C语言类型是 wchar_t

调用函数,继续

注意 printf 将打印到真正标准输出设备,而不是 sys.stdout,因此这些实例只能在控制台提示符下工作,而不能在 IDLEPythonWin 中运行。

>>> printf = libc.printf
>>> printf(b"Hello, %s\n", b"World!")
Hello, World!
14
>>> printf(b"Hello, %S\n", "World!")
Hello, World!
14
>>> printf(b"%d bottles of beer\n", 42)
42 bottles of beer
19
>>> printf(b"%f bottles of beer\n", 42.5)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ArgumentError: argument 2: exceptions.TypeError: Don't know how to convert parameter 2
>>>

正如前面所提到过的,除了整数、字符串以及字节串之外,所有的 Python 类型都必须使用它们对应的 ctypes 类型包装,才能够被正确地转换为所需的C语言类型。

>>> printf(b"An int %d, a double %f\n", 1234, c_double(3.14))
An int 1234, a double 3.140000
31
>>>

使用自定义的数据类型调用函数

你也可以通过自定义 ctypes 参数转换方式来允许自定义类型作为参数。 ctypes 会寻找 _as_parameter_ 属性并使用它作为函数参数。当然,它必须是数字、字符串或者二进制字符串:

>>> class Bottles:
...     def __init__(self, number):
...         self._as_parameter_ = number
...
>>> bottles = Bottles(42)
>>> printf(b"%d bottles of beer\n", bottles)
42 bottles of beer
19
>>>

如果你不想把实例的数据存储到 _as_parameter_ 属性。可以通过定义 property 函数计算出这个属性。

指定必选参数的类型(函数原型)

可以通过设置 argtypes 属性的方法指定从 DLL 中导出函数的必选参数类型。

argtypes 必须是一个 C 数据类型的序列 (这里的 printf 可能不是个好例子,因为它是变长参数,而且每个参数的类型依赖于格式化字符串,不过尝试这个功能也很方便):

>>> printf.argtypes = [c_char_p, c_char_p, c_int, c_double]
>>> printf(b"String '%s', Int %d, Double %f\n", b"Hi", 10, 2.2)
String 'Hi', Int 10, Double 2.200000
37
>>>

指定数据类型可以防止不合理的参数传递(就像 C 函数的原型),并且会自动尝试将参数转换为需要的类型:

>>> printf(b"%d %d %d", 1, 2, 3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ArgumentError: argument 2: exceptions.TypeError: wrong type
>>> printf(b"%s %d %f\n", b"X", 2, 3)
X 2 3.000000
13
>>>

如果你想通过自定义类型传递参数给函数,必须实现 from_param() 类方法,才能够将此自定义类型用于 argtypes 序列。from_param() 类方法接受一个 Python 对象作为函数输入,它应该进行类型检查或者其他必要的操作以保证接收到的对象是合法的,然后返回这个对象,或者它的 _as_parameter_ 属性,或者其他你想要传递给 C 函数的参数。这里也一样,返回的结果必须是整型、字符串、二进制字符串、 ctypes 类型,或者一个具有 _as_parameter_ 属性的对象。

返回类型

默认情况下都会假定函数返回 C int 类型。 其他返回类型可以通过设置函数对象的 restype 属性来指定。

这是个更高级的例子,它调用了 strchr 函数,这个函数接收一个字符串指针以及一个字符作为参数,返回另一个字符串指针。

>>> strchr = libc.strchr
>>> strchr(b"abcdef", ord("d"))  
8059983
>>> strchr.restype = c_char_p    # c_char_p is a pointer to a string
>>> strchr(b"abcdef", ord("d"))
b'def'
>>> print(strchr(b"abcdef", ord("x")))
None
>>>

如果希望避免上述的 ord("x") 调用,可以设置 argtypes 属性,第二个参数就会将单字符的 Python 二进制字符对象转换为 C 字符:

>>> strchr.restype = c_char_p
>>> strchr.argtypes = [c_char_p, c_char]
>>> strchr(b"abcdef", b"d")
'def'
>>> strchr(b"abcdef", b"def")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ArgumentError: argument 2: exceptions.TypeError: one character string expected
>>> print(strchr(b"abcdef", b"x"))
None
>>> strchr(b"abcdef", b"d")
'def'
>>>

如果外部函数返回了一个整数,你也可以使用要给可调用的 Python 对象(比如函数或者类)作为 restype 属性的值。将会以 C 函数返回的 整数 对象作为参数调用这个可调用对象,执行后的结果作为最终函数返回值。这在错误返回值校验和自动抛出异常等方面比较有用。

>>> GetModuleHandle = windll.kernel32.GetModuleHandleA  
>>> def ValidHandle(value):
...     if value == 0:
...         raise WinError()
...     return value
...
>>>
>>> GetModuleHandle.restype = ValidHandle  
>>> GetModuleHandle(None)  
486539264
>>> GetModuleHandle("something silly")  
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in ValidHandle
OSError: [Errno 126] The specified module could not be found.
>>>

WinError 函数可以调用 Windows 的 FormatMessage() API 获取错误码的字符串说明,然后 返回 一个异常。 WinError 接收一个可选的错误码作为参数,如果没有的话,它将调用 GetLastError() 获取错误码。

请注意,使用 errcheck 属性可以实现更强大的错误检查手段;详情请见参考手册。

传递指针(或以引用方式传递形参)

有时候 C 函数接口可能由于要往某个地址写入值,或者数据太大不适合作为值传递,从而希望接收一个 指针 作为数据参数类型。这和 传递参数引用 类似。

ctypes 暴露了 byref() 函数用于通过引用传递参数,使用 pointer() 函数也能达到同样的效果,只不过 pointer() 需要更多步骤,因为它要先构造一个真实指针对象。所以在 Python 代码本身不需要使用这个指针对象的情况下,使用 byref() 效率更高。

>>> i = c_int()
>>> f = c_float()
>>> s = create_string_buffer(b'\000' * 32)
>>> print(i.value, f.value, repr(s.value))
0 0.0 b''
>>> libc.sscanf(b"1 3.14 Hello", b"%d %f %s",
...             byref(i), byref(f), s)
3
>>> print(i.value, f.value, repr(s.value))
1 3.1400001049 b'Hello'
>>>

结构体和联合

结构体和联合必须继承自 ctypes 模块中的 StructureUnion 。子类必须定义 _fields_ 属性。 _fields_ 是一个二元组列表,二元组中包含 field namefield type

type 字段必须是一个 ctypes 类型,比如 c_int,或者其他 ctypes 类型: 结构体、联合、数组、指针。

这是一个简单的 POINT 结构体,它包含名称为 xy 的两个变量,还展示了如何通过构造函数初始化结构体。

>>> from ctypes import *
>>> class POINT(Structure):
...     _fields_ = [("x", c_int),
...                 ("y", c_int)]
...
>>> point = POINT(10, 20)
>>> print(point.x, point.y)
10 20
>>> point = POINT(y=5)
>>> print(point.x, point.y)
0 5
>>> POINT(1, 2, 3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: too many initializers
>>>

当然,你可以构造更复杂的结构体。一个结构体可以通过设置 type 字段包含其他结构体或者自身。

这是以一个 RECT 结构体,他包含了两个 POINT ,分别叫 upperleftlowerright:

>>> class RECT(Structure):
...     _fields_ = [("upperleft", POINT),
...                 ("lowerright", POINT)]
...
>>> rc = RECT(point)
>>> print(rc.upperleft.x, rc.upperleft.y)
0 5
>>> print(rc.lowerright.x, rc.lowerright.y)
0 0
>>>

嵌套结构体可以通过几种方式构造初始化:

>>> r = RECT(POINT(1, 2), POINT(3, 4))
>>> r = RECT((1, 2), (3, 4))

可以通过 获取字段 descriptor ,它能提供很多有用的调试信息。

>>> print(POINT.x)
<Field type=c_long, ofs=0, size=4>
>>> print(POINT.y)
<Field type=c_long, ofs=4, size=4>
>>>

警告

ctypes 不支持带位域的结构体、联合以值的方式传给函数。这可能在 32 位 x86 平台上可以正常工作,但是对于一般情况,这种行为是未定义的。带位域的结构体、联合应该总是通过指针传递给函数。

结构体/联合字段对齐及字节顺序

默认情况下,结构体和联合的字段与 C 的字节对齐是一样的。也可以在定义子类的时候指定类的 _pack_ 属性来覆盖这种行为。 它必须设置为一个正整数,表示字段的最大对齐字节。这和 MSVC 中的 #pragma pack(n) 功能一样。

ctypes 中的结构体和联合使用的是本地字节序。要使用非本地字节序,可以使用 BigEndianStructure, LittleEndianStructure, BigEndianUnion, and LittleEndianUnion 作为基类。这些类不能包含指针字段。

结构体和联合中的位域

结构体和联合中是可以包含位域字段的。位域只能用于整型字段,位长度通过 _fields_ 中的第三个参数指定:

>>> class Int(Structure):
...     _fields_ = [("first_16", c_int, 16),
...                 ("second_16", c_int, 16)]
...
>>> print(Int.first_16)
<Field type=c_long, ofs=0:0, bits=16>
>>> print(Int.second_16)
<Field type=c_long, ofs=0:16, bits=16>
>>>

数组

数组是一个序列,包含指定个数元素,且必须类型相同。

创建数组类型的推荐方式是使用一个类型乘以一个正数:

TenPointsArrayType = POINT * 10

下面是一个构造的数据案例,结构体中包含了4个 POINT 和一些其他东西。

>>> from ctypes import *
>>> class POINT(Structure):
...     _fields_ = ("x", c_int), ("y", c_int)
...
>>> class MyStruct(Structure):
...     _fields_ = [("a", c_int),
...                 ("b", c_float),
...                 ("point_array", POINT * 4)]
>>>
>>> print(len(MyStruct().point_array))
4
>>>

和平常一样,通过调用它创建实例:

arr = TenPointsArrayType()
for pt in arr:
    print(pt.x, pt.y)

以上代码会打印几行 0 0 ,因为数组内容被初始化为 0.

也能通过指定正确类型的数据来初始化:

>>> from ctypes import *
>>> TenIntegers = c_int * 10
>>> ii = TenIntegers(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
>>> print(ii)
<c_long_Array_10 object at 0x...>
>>> for i in ii: print(i, end=" ")
...
1 2 3 4 5 6 7 8 9 10
>>>

指针

可以将 ctypes 类型数据传入 pointer() 函数创建指针:

>>> from ctypes import *
>>> i = c_int(42)
>>> pi = pointer(i)
>>>

指针实例拥有 contents 属性,它返回指针指向的真实对象,如上面的 i 对象:

>>> pi.contents
c_long(42)
>>>

注意 ctypes 并没有 OOR (返回原始对象), 每次访问这个属性时都会构造返回一个新的相同对象:

>>> pi.contents is i
False
>>> pi.contents is pi.contents
False
>>>

将这个指针的 contents 属性赋值为另一个 c_int 实例将会导致该指针指向该实例的内存地址:

>>> i = c_int(99)
>>> pi.contents = i
>>> pi.contents
c_long(99)
>>>

指针对象也可以通过整数下标进行访问:

>>> pi[0]
99
>>>

通过整数下标赋值可以改变指针所指向的真实内容:

>>> print(i)
c_long(99)
>>> pi[0] = 22
>>> print(i)
c_long(22)
>>>

使用 0 以外的索引也是合法的,但是你必须确保知道自己为什么这么做,就像 C 语言中: 你可以访问或者修改任意内存内容。 通常只会在函数接收指针是才会使用这种特性,而且你 知道 这个指针指向的是一个数组而不是单个值。

内部细节, pointer() 函数不只是创建了一个指针实例,它首先创建了一个指针 类型 。这是通过调用 POINTER() 函数实现的,它接收 ctypes 类型为参数,返回一个新的类型:

>>> PI = POINTER(c_int)
>>> PI
<class 'ctypes.LP_c_long'>
>>> PI(42)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: expected c_long instead of int
>>> PI(c_int(42))
<ctypes.LP_c_long object at 0x...>
>>>

无参调用指针类型可以创建一个 NULL 指针。 NULL 指针的布尔值是 False

>>> null_ptr = POINTER(c_int)()
>>> print(bool(null_ptr))
False
>>>

解引用指针的时候, ctypes 会帮你检测是否指针为 NULL (但是解引用无效的 非 NULL 指针仍会导致 Python 崩溃):

>>> null_ptr[0]
Traceback (most recent call last):
    ....
ValueError: NULL pointer access
>>>
>>> null_ptr[0] = 1234
Traceback (most recent call last):
    ....
ValueError: NULL pointer access
>>>

类型转换

通常情况下, ctypes 具有严格的类型检查。这代表着, 如果在函数 argtypes 中或者结构体定义成员中有 POINTER(c_int) 类型,只有相同类型的实例才会被接受。 也有一些例外。比如,你可以传递兼容的数组实例给指针类型。所以,对于 POINTER(c_int) ,ctypes 也可以接受 c_int 类型的数组:

>>> class Bar(Structure):
...     _fields_ = [("count", c_int), ("values", POINTER(c_int))]
...
>>> bar = Bar()
>>> bar.values = (c_int * 3)(1, 2, 3)
>>> bar.count = 3
>>> for i in range(bar.count):
...     print(bar.values[i])
...
1
2
3
>>>

另外,如果一个函数 argtypes 列表中的参数显式的定义为指针类型(如 POINTER(c_int) ),指针所指向的 类型 (这个例子中是 c_int )也可以传递给函数。ctypes 会自动调用对应的 byref() 转换。

可以给指针内容赋值为 None 将其设置为 Null

>>> bar.values = None
>>>

有时候你拥有一个不兼容的类型。 在 C 中,你可以将一个类型强制转换为另一个。 ctypes 中的 a cast() 函数提供了相同的功能。 上面的结构体 Barvalue 字段接收 POINTER(c_int) 指针或者 c_int 数组,但是不能接受其他类型的实例:

>>> bar.values = (c_byte * 4)()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: incompatible types, c_byte_Array_4 instance instead of LP_c_long instance
>>>

这种情况下, 需要手动使用 cast() 函数。

cast() 函数可以将一个指针实例强制转换为另一种 ctypes 类型。 cast() 接收两个参数,一个 ctypes 指针对象或者可以被转换为指针的其他类型对象,和一个 ctypes 指针类型。 返回第二个类型的一个实例,该返回实例和第一个参数指向同一片内存空间:

>>> a = (c_byte * 4)()
>>> cast(a, POINTER(c_int))
<ctypes.LP_c_long object at ...>
>>>

所以 cast() 可以用来给结构体 Barvalues 字段赋值:

>>> bar = Bar()
>>> bar.values = cast((c_byte * 4)(), POINTER(c_int))
>>> print(bar.values[0])
0
>>>

不完整类型

不完整类型 即还没有定义成员的结构体、联合或者数组。在 C 中,它们通常用于前置声明,然后在后面定义:

struct cell; /* forward declaration */
struct cell {
    char *name;
    struct cell *next;
};

直接翻译成 ctypes 的代码如下,但是这行不通:

>>> class cell(Structure):
...     _fields_ = [("name", c_char_p),
...                 ("next", POINTER(cell))]
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in cell
NameError: name 'cell' is not defined
>>>

因为新的 cell 类 在 class 语句结束之前还没有完成定义。在 ctypes 中,我们可以先定义 cell 类,在 class 语句结束之后再设置 _fields_ 属性:

>>> from ctypes import *
>>> class cell(Structure):
...     pass
...
>>> cell._fields_ = [("name", c_char_p),
...                  ("next", POINTER(cell))]
>>>

让我们试试。我们定义两个 cell 实例,让它们互相指向对方,然后通过指针链式访问几次:

>>> c1 = cell()
>>> c1.name = b"foo"
>>> c2 = cell()
>>> c2.name = b"bar"
>>> c1.next = pointer(c2)
>>> c2.next = pointer(c1)
>>> p = c1
>>> for i in range(8):
...     print(p.name, end=" ")
...     p = p.next[0]
...
foo bar foo bar foo bar foo bar
>>>

回调函数

ctypes 允许创建一个指向 Python 可调用对象的 C 函数。它们有时候被称为 回调函数

首先,你必须为回调函数创建一个类,这个类知道调用约定,包括返回值类型以及函数接收的参数类型及个数。

CFUNCTYPE() 工厂函数使用 cdecl 调用约定创建回调函数类型。在 Windows 上, WINFUNCTYPE() 工厂函数使用 stdcall 调用约定为回调函数创建类型。

这些工厂函数的第一个参数是返回值类型,回调函数的参数类型作为剩余参数。

这里展示一个使用 C 标准库函数 qsort() 的例子,它使用一个回调函数对数据进行排序。 qsort() 将用来给整数数组排序:

>>> IntArray5 = c_int * 5
>>> ia = IntArray5(5, 1, 7, 33, 99)
>>> qsort = libc.qsort
>>> qsort.restype = None
>>>

qsort() 必须接收的参数,一个指向待排序数据的指针,元素个数,每个元素的大小,以及一个指向排序函数的指针,即回调函数。然后回调函数接收两个元素的指针,如果第一个元素小于第二个,则返回一个负整数,如果相等则返回0,否则返回一个正整数。

所以,我们的回调函数要接收两个整数指针,返回一个整数。首先我们创建回调函数的 类型

>>> CMPFUNC = CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))
>>>

首先,这是一个简单的回调,它会显示传入的值:

>>> def py_cmp_func(a, b):
...     print("py_cmp_func", a[0], b[0])
...     return 0
...
>>> cmp_func = CMPFUNC(py_cmp_func)
>>>

结果:

>>> qsort(ia, len(ia), sizeof(c_int), cmp_func)  
py_cmp_func 5 1
py_cmp_func 33 99
py_cmp_func 7 33
py_cmp_func 5 7
py_cmp_func 1 7
>>>

现在我们可以比较两个元素并返回有用的结果了:

>>> def py_cmp_func(a, b):
...     print("py_cmp_func", a[0], b[0])
...     return a[0] - b[0]
...
>>>
>>> qsort(ia, len(ia), sizeof(c_int), CMPFUNC(py_cmp_func)) 
py_cmp_func 5 1
py_cmp_func 33 99
py_cmp_func 7 33
py_cmp_func 1 7
py_cmp_func 5 7
>>>

我们可以轻易地验证,现在数组是有序的了:

>>> for i in ia: print(i, end=" ")
...
1 5 7 33 99
>>>

这些工厂函数可以当作装饰器工厂,所以可以这样写:

>>> @CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))
... def py_cmp_func(a, b):
...     print("py_cmp_func", a[0], b[0])
...     return a[0] - b[0]
...
>>> qsort(ia, len(ia), sizeof(c_int), py_cmp_func)
py_cmp_func 5 1
py_cmp_func 33 99
py_cmp_func 7 33
py_cmp_func 1 7
py_cmp_func 5 7
>>>

注解

请确保你维持的 CFUNCTYPE() 对象的引用周期与它们在 C 代码中的使用期一样长。 ctypes 不会确保这一点,如果不这样做,它们可能会被垃圾回收,导致程序在执行回调函数时发生崩溃。

注意,如果回调函数在Python之外的另外一个线程使用(比如,外部代码调用这个回调函数), ctypes 会在每一次调用上创建一个虚拟 Python 线程。这个行为在大多数情况下是合理的,但也意味着如果有数据使用 threading.local 方式存储,将无法访问,就算它们是在同一个 C 线程中调用的 。

访问 dll 的导出变量

一些动态链接库不仅仅导出函数,也会导出变量。一个例子就是 Python 库本身的 Py_OptimizeFlag ,根据启动选项 -O-OO 的不同,它是值可能为 0、1、2 的整型。

ctypes 可以通过 in_dll() 类方法访问这类变量 。 pythonapi 是用于访问 Python C 接口的预定义符号:

>>> opt_flag = c_int.in_dll(pythonapi, "Py_OptimizeFlag")
>>> print(opt_flag)
c_long(0)
>>>

如果解释器使用 -O 选项启动,这个例子会打印 c_long(1) , 如果使用 -OO 启动,则会打印 c_long(2)

一个扩展例子, 同时也展示了使用指针访问 Python 导出的 PyImport_FrozenModules 指针对象。

对文档中这个值的解释说明

该指针被初始化为指向 struct _frozen 数组,以 NULL 或者 0 作为结束标记。当一个冻结模块被导入,首先要在这个表中搜索。第三方库可以以此来提供动态创建的冻结模块集合。

这足以证明修改这个指针是很有用的。为了让实例大小不至于太长,这里只展示如何使用 ctypes 读取这个表:

>>> from ctypes import *
>>>
>>> class struct_frozen(Structure):
...     _fields_ = [("name", c_char_p),
...                 ("code", POINTER(c_ubyte)),
...                 ("size", c_int)]
...
>>>

我们定义了 struct _frozen 数据类型,接着就可以获取这张表的指针了:

>>> FrozenTable = POINTER(struct_frozen)
>>> table = FrozenTable.in_dll(pythonapi, "PyImport_FrozenModules")
>>>

由于 table 是指向 struct_frozen 数组的 指针 ,我们可以遍历它,只不过需要自己判断循环是否结束,因为指针本身并不包含长度。它早晚会因为访问到野指针或者什么的把自己搞崩溃,所以我们最好在遇到 NULL 后就让它退出循环:

>>> for item in table:
...     if item.name is None:
...         break
...     print(item.name.decode("ascii"), item.size)
...
_frozen_importlib 31764
_frozen_importlib_external 41499
__hello__ 161
__phello__ -161
__phello__.spam 161
>>>

Python 的冻结模块和冻结包(由负 size 成员表示)并不是广为人知的事情,它们仅仅用于实验。例如,可以使用 import __hello__ 尝试一下这个功能。

意外

ctypes 也有自己的边界,有时候会发生一些意想不到的事情。

比如下面的例子:

>>> from ctypes import *
>>> class POINT(Structure):
...     _fields_ = ("x", c_int), ("y", c_int)
...
>>> class RECT(Structure):
...     _fields_ = ("a", POINT), ("b", POINT)
...
>>> p1 = POINT(1, 2)
>>> p2 = POINT(3, 4)
>>> rc = RECT(p1, p2)
>>> print(rc.a.x, rc.a.y, rc.b.x, rc.b.y)
1 2 3 4
>>> # now swap the two points
>>> rc.a, rc.b = rc.b, rc.a
>>> print(rc.a.x, rc.a.y, rc.b.x, rc.b.y)
3 4 3 4
>>>

嗯。我们预想应该打印 3 4 1 2 。但是为什么呢? 这是 rc.a, rc.b = rc.b, rc.a 这行代码展开后的步骤:

>>> temp0, temp1 = rc.b, rc.a
>>> rc.a = temp0
>>> rc.b = temp1
>>>

注意 temp0temp1 对象始终引用了对象 rc 的内容。然后执行 rc.a = temp0 会把 temp0 的内容拷贝到 rc 的空间。这也改变了 temp1 的内容。最终导致赋值语句 rc.b = temp1 没有产生预想的效果。

记住,访问被包含在结构体、联合、数组中的对象并不会将其 复制 出来,而是得到了一个代理对象,它是对根对象的内部内容的一层包装。

下面是另一个可能和预期有偏差的例子:

>>> s = c_char_p()
>>> s.value = b"abc def ghi"
>>> s.value
b'abc def ghi'
>>> s.value is s.value
False
>>>

注解

使用 c_char_p 实例化的对象只能将其值设置为 bytes 或者整数。

为什么这里打印了 False ? ctypes 实例是一些内存块加上一些用于访问这些内存块的 descriptor 组成。将 Python 对象存储在内存块并不会存储对象本身,而是存储了对象的 内容 。每次访问对象的内容都会构造一个新的 Python 对象。

变长数据类型

ctypes 对变长数组和结构体提供了一些支持 。

The resize() function can be used to resize the memory buffer of an existing ctypes object. The function takes the object as first argument, and the requested size in bytes as the second argument. The memory block cannot be made smaller than the natural memory block specified by the objects type, a ValueError is raised if this is tried:

>>> short_array = (c_short * 4)()
>>> print(sizeof(short_array))
8
>>> resize(short_array, 4)
Traceback (most recent call last):
    ...
ValueError: minimum size is 8
>>> resize(short_array, 32)
>>> sizeof(short_array)
32
>>> sizeof(type(short_array))
8
>>>

这非常好,但是要怎么访问数组中额外的元素呢?因为数组类型已经定义包含4个元素,导致我们访问新增元素时会产生以下错误:

>>> short_array[:]
[0, 0, 0, 0]
>>> short_array[7]
Traceback (most recent call last):
    ...
IndexError: invalid index
>>>

使用 ctypes 访问变长数据类型的一个可行方法是利用 Python 的动态特性,根据具体情况,在知道这个数据的大小后,(重新)指定这个数据的类型。

ctypes 参考手册

寻找动态链接库

在编译型语言中,动态链接库会在编译、链接或者程序运行时访问。

The purpose of the find_library() function is to locate a library in a way similar to what the compiler or runtime loader does (on platforms with several versions of a shared library the most recent should be loaded), while the ctypes library loaders act like when a program is run, and call the runtime loader directly.

ctypes.util 模块提供了一个函数,可以帮助确定需要加载的库。

ctypes.util.find_library(name)

尝试寻找一个库然后返回其路径名, name 是库名称, 且去除了 lib 等前缀和 .so.dylib 、版本号等后缀(这是 posix 连接器 -l 选项使用的格式)。如果没有找到对应的库,则返回 None

确切的功能取决于系统。

在 Linux 上, find_library() 会尝试运行外部程序(/sbin/ldconfig, gcc, objdump 以及 ld) 来寻找库文件。返回库文件的文件名。

在 3.6 版更改: 在Linux 上,如果其他方式找不到的话,会使用环境变量 LD_LIBRARY_PATH 搜索动态链接库。

这是一些例子:

>>> from ctypes.util import find_library
>>> find_library("m")
'libm.so.6'
>>> find_library("c")
'libc.so.6'
>>> find_library("bz2")
'libbz2.so.1.0'
>>>

On macOS, find_library() tries several predefined naming schemes and paths to locate the library, and returns a full pathname if successful:

>>> from ctypes.util import find_library
>>> find_library("c")
'/usr/lib/libc.dylib'
>>> find_library("m")
'/usr/lib/libm.dylib'
>>> find_library("bz2")
'/usr/lib/libbz2.dylib'
>>> find_library("AGL")
'/System/Library/Frameworks/AGL.framework/AGL'
>>>

在 Windows 上, find_library() 在系统路径中搜索,然后返回全路径,但是如果没有预定义的命名方案, find_library("c") 调用会返回 None

使用 ctypes 包装动态链接库,更好的方式 可能 是在开发的时候就确定名称,然后硬编码到包装模块中去,而不是在运行时使用 find_library() 寻找库。

加载动态链接库

有很多方式可以将动态链接库加载到 Python 进程。其中之一是实例化以下类的其中一个:

class ctypes.CDLL(name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False, winmode=0)

此类的实例即已加载的动态链接库。库中的函数使用标准 C 调用约定,并假定返回 int 。

在 Windows 上创建 CDLL 实例可能会失败,即使 DLL 名称确实存在。 当某个被加载 DLL 所依赖的 DLL 未找到时,将引发 OSError 错误并附带消息 “[WinError 126] The specified module could not be found”. 此错误消息不包含缺失 DLL 的名称,因为 Windows API 并不会返回此类信息,这使得此错误难以诊断。 要解决此错误并确定是哪一个 DLL 未找到,你需要找出所依赖的 DLL 列表并使用 Windows 调试与跟踪工具确定是哪一个未找到。

参见

Microsoft DUMPBIN 工具 — 一个用于查找 DLL 依赖的工具。

class ctypes.OleDLL(name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False, winmode=0)

仅 Windows : 此类的实例即加载好的动态链接库,其中的函数使用 stdcall 调用约定,并且假定返回 windows 指定的 HRESULT 返回码。 HRES

ULT 的值包含的信息说明函数调用成功还是失败,以及额外错误码。 如果返回值表示失败,会自动抛出 OSError 异常。

在 3.3 版更改: 以前是引发 WindowsError

class ctypes.WinDLL(name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False, winmode=0)

仅 Windows: 此类的实例即加载好的动态链接库,其中的函数使用 stdcall 调用约定,并假定默认返回 int 。

在 Windows CE 上,只能使用 stdcall 调用约定,为了方便, WinDLLOleDLL 在这个平台上都使用标准调用约定。

调用动态库导出的函数之前,Python会释放 global interpreter lock ,并在调用后重新获取。

class ctypes.PyDLL(name, mode=DEFAULT_MODE, handle=None)

这个类实例的行为与 CDLL 类似,只不过 不会 在调用函数的时候释放 GIL 锁,且调用结束后会检查 Python 错误码。 如果错误码被设置,会抛出一个 Python 异常。

所以,它只在直接调用 Python C 接口函数的时候有用。

通过使用至少一个参数(共享库的路径名)调用它们,可以实例化所有这些类。也可以传入一个已加载的动态链接库作为 handler 参数,其他情况会调用系统底层的 dlopenLoadLibrary 函数将库加载到进程,并获取其句柄。

mode 可以指定库加载方式。 在 Windows 上, 会忽略 mode ,在 posix 系统上, 总是会加上 RTLD_NOW ,且无法配置。

use_errno 参数如果设置为 true,可以启用ctypes的机制,通过一种安全的方法获取系统的 errno 错误码。 ctypes 维护了一个线程局部变量,它是系统 errno 的一份拷贝;如果调用了使用 use_errno=True 创建的外部函数, errno 的值会与 ctypes 自己拷贝的那一份进行交换,函数执行完后立即再交换一次。

The function ctypes.get_errno() returns the value of the ctypes private copy, and the function ctypes.set_errno() changes the ctypes private copy to a new value and returns the former value.

use_last_error 参数如果设置为 true,可以在 Windows 上启用相同的策略,它是通过 Windows API 函数 GetLastError()SetLastError() 管理的。 ctypes.get_last_error()ctypes.set_last_error() 可用于获取和设置 ctypes 自己维护的 windows 错误码拷贝。

winmode 参数用于在 Windows 平台上指定库的加载方式( 因为 mode 会被忽略)。他接受任何与 Win32 API 的 LoadLibraryEx 的标志兼容的值作为参数。省略时,默认设置使用最安全的DLL加载的标志,以避免DLL劫持等问题。传入 DLL 的全路径是保证正确加载库及其依赖最安全的方法。

在 3.8 版更改: 增加了 winmode 参数。

ctypes.RTLD_GLOBAL

用于 mode 参数的标识值。在此标识不可用的系统上,它被定义为整数0。

ctypes.RTLD_LOCAL

Flag to use as mode parameter. On platforms where this is not available, it is the same as RTLD_GLOBAL.

ctypes.DEFAULT_MODE

加载动态链接库的默认模式。在 OSX 10.3 上,它是 RTLD_GLOBAL ,其余系统上是 RTLD_LOCAL

这些类的实例没有共用方法。动态链接库的导出函数可以通过属性或者索引的方式访问。注意,通过属性的方式访问会缓存这个函数,因而每次访问它时返回的都是同一个对象。另一方面,通过索引访问,每次都会返回一个新的对象:

>>> from ctypes import CDLL
>>> libc = CDLL("libc.so.6")  # On Linux
>>> libc.time == libc.time
True
>>> libc['time'] == libc['time']
False

还有下面这些属性可用,他们的名称以下划线开头,以避免和导出函数重名:

PyDLL._handle

用于访问库的系统句柄。

PyDLL._name

传入构造函数的库名称。

共享库也可以通用使用一个预制对象来加载,这种对象是 LibraryLoader 类的实例,具体做法或是通过调用 LoadLibrary() 方法,或是通过将库作为加载器实例的属性来提取。

class ctypes.LibraryLoader(dlltype)

加载共享库的类。 dlltype 应当为 CDLL, PyDLL, WinDLLOleDLL 类型之一。

__getattr__() 具有特殊的行为:它允许通过将一个共享库作为库加载器实例的属性进行访问来加载它。 加载结果将被缓存,因此重复的属性访问每次都会返回相同的库。

  • LoadLibrary(name)

    加载一个共享库到进程中并将其返回。 此方法总是返回一个新的库实例。

可用的预制库加载器有如下这些:

ctypes.cdll

创建 CDLL 实例。

ctypes.windll

仅限 Windows:创建 WinDLL 实例.

ctypes.oledll

仅限 Windows:创建 OleDLL 实例。

ctypes.pydll

创建 PyDLL 实例。

要直接访问 C Python api,可以使用一个现成的 Python 共享库对象:

ctypes.pythonapi

一个 PyDLL 的实例,它将 Python C API 函数作为属性公开。 请注意所有这些函数都应返回 C int,当然这也不是绝对的,因此你必须分配正确的 restype 属性以使用这些函数。

引发一个 审计事件 ctypes.dlopen,附带参数 name

引发一个审计事件 ctypes.dlsym,附带参数 library, name

引发一个审计事件 ctypes.dlsym/handle,附带参数 handle, name

外部函数

正如之前小节的说明,外部函数可作为被加载共享库的属性来访问。 用此方式创建的函数对象默认接受任意数量的参数,接受任意 ctypes 数据实例作为参数,并且返回库加载器所指定的默认结果类型。 它们是一个私有类的实例:

class ctypes._FuncPtr

C 可调用外部函数的基类。

外部函数的实例也是兼容 C 的数据类型;它们代表 C 函数指针。

此行为可通过对外部函数对象的特殊属性赋值来自定义。

  • restype

    赋值为一个 ctypes 类型来指定外部函数的结果类型。 使用 None 表示 void,即不返回任何结果的函数。

    赋值为一个不为 ctypes 类型的可调用 Python 对象也是可以的,在此情况下函数应返回 C int,该可调用对象将附带此整数被调用,以允许进一步的处理或错误检测。 这种用法已被弃用,为了更灵活的后续处理或错误检测请使用一个 ctypes 数据类型作为 restype 并将 errcheck 属性赋值为一个可调用对象。

  • argtypes

    赋值为一个 ctypes 类型的元组来指定函数所接受的参数类型。 使用 stdcall 调用规范的函数只能附带与此元组长度相同数量的参数进行调用;使用 C 调用规范的函数还可接受额外的未指明参数。

    当外部函数被调用时,每个实际参数都会被传给 argtypes 元组中条目的 from_param() 类方法,此方法允许将实际参数适配为此外部函数所接受的对象。 例如,argtypes 元组中的 c_char_p 条目将使用 ctypes 约定规则把作为参数传入的字符串转换为字节串对象。

    新增:现在可以将不是 ctypes 类型的条目放入 argtypes,但每个条目都必须具有 from_param() 方法用于返回可作为参数的值(整数、字符串、ctypes 实例)。 这样就允许定义可将自定义对象适配为函数形参的适配器。

  • errcheck

    将一个 Python 函数或其他可调用对象赋值给此属性。 该可调用对象将附带三个及以上的参数被调用。

    • callable(result, func, arguments)

      result 是外部函数返回的结果,由 restype 属性指明。

      func 是外部函数对象本身,这样就允许重新使用相同的可调用对象来对多个函数进行检查或后续处理。

      arguments 是一个包含最初传递给函数调用的形参的元组,这样就允许对所用参数的行为进行特别处理。

    此函数所返回的对象将会由外部函数调用返回,但它还可以在外部函数调用失败时检查结果并引发异常。

exception ctypes.ArgumentError

此异常会在外部函数无法对某个传入参数执行转换时被引发。

引发一个审计事件 ctypes.seh_exception 并附带参数 code

引发一个审计事件 ctypes.call_function,附带参数 func_pointer, arguments

函数原型

外部函数也可通过实例化函数原型来创建。 函数原型类似于 C 中的函数原型;它们在不定义具体实现的情况下描述了一个函数(返回类型、参数类型、调用约定)。 工厂函数必须使用函数所需要的结果类型和参数类型来调用,并可被用作装饰器工厂函数,在此情况下可以通过 @wrapper 语法应用于函数。

ctypes.CFUNCTYPE(restype, \argtypes, use_errno=False, use_last_error=False*)

返回的函数原型会创建使用标准 C 调用约定的函数。 该函数在调用过程中将释放 GIL。 如果 use_errno 设为真值,则在调用之前和之后系统 errno 变量的 ctypes 私有副本会与真正的 errno 值进行交换;use_last_error 会为 Windows 错误码执行同样的操作。

ctypes.WINFUNCTYPE(restype, \argtypes, use_errno=False, use_last_error=False*)

仅限 Windows only:返回的函数原型会创建使用 stdcall 调用约定的函数,但在 Windows CE 上 WINFUNCTYPE() 则会与 CFUNCTYPE() 相同。 该函数在调用过程中将释放 GIL。 use_errnouse_last_error 具有与前面相同的含义。

ctypes.PYFUNCTYPE(restype, \argtypes*)

返回的函数原型会创建使用 Python 调用约定的函数。 该函数在调用过程中将 不会 释放 GIL。

这些工厂函数所创建的函数原型可通过不同的方式来实例化,具体取决于调用中的类型与数量:

prototype(address)

在指定地址上返回一个外部函数,地址值必须为整数。

prototype(callable)

基于 Python callable 创建一个 C 可调用函数(回调函数)。

prototype(func_spec[, paramflags])

返回由一个共享库导出的外部函数。 func_spec 必须为一个 2 元组 (name_or_ordinal, library)。 第一项是字符串形式的所导出函数名称,或小整数形式的所导出函数序号。 第二项是该共享库实例。

prototype(vtbl_index, name[, paramflags[, iid]])

返回将调用一个 COM 方法的外部函数。 vtbl_index 虚拟函数表中的索引。 name 是 COM 方法的名称。 iid 是可选的指向接口标识符的指针,它被用于扩展的错误报告。

COM 方法使用特殊的调用约定:除了在 argtypes 元组中指定的形参,它们还要求一个指向 COM 接口的指针作为第一个参数。

可选的 paramflags 形参会创建相比上述特性具有更多功能的外部函数包装器。

paramflags 必须为一个与 argtypes 长度相同的元组。

此元组中的每一项都包含有关形参的更多信息,它必须为包含一个、两个或更多条目的元组。

第一项是包含形参指令旗标组合的整数。

1

指定函数的一个输入形参。

2

输出形参。 外部函数会填入一个值。

4

默认为整数零值的输入形参。

可选的第二项是字符串形式的形参名称。 如果指定此项,则可以使用该形参名称来调用外部函数。

可选的第三项是该形参的默认值。

这个例子演示了如何包装 Windows 的 MessageBoxW 函数以使其支持默认形参和已命名参数。 相应 windows 头文件的 C 声明是这样的:

WINUSERAPI int WINAPI
MessageBoxW(
    HWND hWnd,
    LPCWSTR lpText,
    LPCWSTR lpCaption,
    UINT uType);

这是使用 ctypes 的包装:

>>> from ctypes import c_int, WINFUNCTYPE, windll
>>> from ctypes.wintypes import HWND, LPCWSTR, UINT
>>> prototype = WINFUNCTYPE(c_int, HWND, LPCWSTR, LPCWSTR, UINT)
>>> paramflags = (1, "hwnd", 0), (1, "text", "Hi"), (1, "caption", "Hello from ctypes"), (1, "flags", 0)
>>> MessageBox = prototype(("MessageBoxW", windll.user32), paramflags)

现在 MessageBox 外部函数可以通过以下方式来调用:

>>> MessageBox()
>>> MessageBox(text="Spam, spam, spam")
>>> MessageBox(flags=2, text="foo bar")

第二个例子演示了输出形参。 这个 win32 GetWindowRect 函数通过将指定窗口的维度拷贝至调用者必须提供的 RECT 结构体来提取这些值。 这是相应的 C 声明:

WINUSERAPI BOOL WINAPI
GetWindowRect(
     HWND hWnd,
     LPRECT lpRect);

这是使用 ctypes 的包装:

>>> from ctypes import POINTER, WINFUNCTYPE, windll, WinError
>>> from ctypes.wintypes import BOOL, HWND, RECT
>>> prototype = WINFUNCTYPE(BOOL, HWND, POINTER(RECT))
>>> paramflags = (1, "hwnd"), (2, "lprect")
>>> GetWindowRect = prototype(("GetWindowRect", windll.user32), paramflags)
>>>

带有输出形参的函数如果输出形参存在单一值则会自动返回该值,或是当输出形参存在多个值时返回包含这些值的元组,因此当 GetWindowRect 被调用时现在将返回一个 RECT 实例。

输出形参可以与 errcheck 协议相结合以执行进一步的输出处理与错误检查。 Win32 GetWindowRect API 函数返回一个 BOOL 来表示成功或失败,因此此函数可执行错误检查,并在 API 调用失败时引发异常:

>>> def errcheck(result, func, args):
...     if not result:
...         raise WinError()
...     return args
...
>>> GetWindowRect.errcheck = errcheck
>>>

如果 errcheck 不加更改地返回它所接收的参数元组,则 ctypes 会继续对输出形参执行常规处理。 如果你希望返回一个窗口坐标的元组而非 RECT 实例,你可以从函数中提取这些字段并返回它们,常规处理将不会再执行:

>>> def errcheck(result, func, args):
...     if not result:
...         raise WinError()
...     rc = args[1]
...     return rc.left, rc.top, rc.bottom, rc.right
...
>>> GetWindowRect.errcheck = errcheck
>>>

工具函数

ctypes.addressof(obj)

以整数形式返回内存缓冲区地址。 obj 必须为一个 ctypes 类型的实例。

引发一个 审计事件 ctypes.addressof,附带参数 obj

ctypes.alignment(obj_or_type)

返回一个 ctypes 类型的对齐要求。 obj_or_type 必须为一个 ctypes 类型或实例。

ctypes.byref(obj[, offset])

返回指向 obj 的轻量指针,该对象必须为一个 ctypes 类型的实例。 offset 默认值为零,且必须为一个将被添加到内部指针值的整数。

byref(obj, offset) 对应于这段 C 代码:

(((char *)&obj) + offset)

返回的对象只能被用作外部函数调用形参。 它的行为类似于 pointer(obj),但构造起来要快很多。

ctypes.cast(obj, type)

此函数类似于 C 的强制转换运算符。 它返回一个 type 的新实例,该实例指向与 obj 相同的内存块。 type 必须为指针类型,而 obj 必须为可以被作为指针来解读的对象。

ctypes.create_string_buffer(init_or_size, size=None)

此函数会创建一个可变的字符缓冲区。 返回的对象是一个 c_char 的 ctypes 数组。

init_or_size 必须是一个指明数组大小的整数,或者是一个将被用来初始化数组条目的字节串对象。

如果将一个字节串对象指定为第一个参数,则将使缓冲区大小比其长度多一项以便数组的最后一项为一个 NUL 终结符。 可以传入一个整数作为第二个参数以允许在不使用字节串长度的情况下指定数组大小。

引发一个 审计事件 ctypes.create_string_buffer,附带参数 init, size

ctypes.create_unicode_buffer(init_or_size, size=None)

此函数会创建一个可变的 unicode 字符缓冲区。 返回的对象是一个 c_wchar 的 ctypes 数组。

init_or_size 必须是一个指明数组大小的整数,或者是一个将被用来初始化数组条目的字符串。

如果将一个字符串指定为第一个参数,则将使缓冲区大小比其长度多一项以便数组的最后一项为一个 NUL 终结符。 可以传入一个整数作为第二个参数以允许在不使用字符串长度的情况下指定数组大小。

引发一个 审计事件 ctypes.create_unicode_buffer,附带参数 init, size

ctypes.DllCanUnloadNow()

仅限 Windows:此函数是一个允许使用 ctypes 实现进程内 COM 服务的钩子。 它将由 _ctypes 扩展 dll 所导出的 DllCanUnloadNow 函数来调用。

ctypes.DllGetClassObject()

仅限 Windows:此函数是一个允许使用 ctypes 实现进程内 COM 服务的钩子。 它将由 _ctypes 扩展 dll 所导出的 DllGetClassObject 函数来调用。

ctypes.util.find_library(name)

尝试寻找一个库并返回路径名称。 name 是库名称并且不带任何前缀如 lib 以及后缀如 .so.dylib 或版本号(形式与 posix 链接器选项 -l 所用的一致)。 如果找不到库,则返回 None

确切的功能取决于系统。

ctypes.util.find_msvcrt()

仅限 Windows:返回 Python 以及扩展模块所使用的 VC 运行时库的文件名。 如果无法确定库名称,则返回 None

如果你需要通过调用 free(void *) 来释放内存,例如某个扩展模块所分配的内存,重要的一点是你应当使用分配内存的库中的函数。

ctypes.FormatError([code])

仅限 Windows:返回错误码 code 的文本描述。 如果未指定错误码,则会通过调用 Windows api 函数 GetLastError 来获得最新的错误码。

ctypes.GetLastError()

仅限 Windows:返回 Windows 在调用线程中设置的最新错误码。 此函数会直接调用 Windows GetLastError() 函数,它并不返回错误码的 ctypes 私有副本。

ctypes.get_errno()

返回调用线程中系统 errno 变量的 ctypes 私有副本的当前值。

引发一个 审计事件 ctypes.get_errno,不附带任何参数。

ctypes.get_last_error()

仅限 Windows:返回调用线程中系统 LastError 变量的 ctypes 私有副本的当前值。

引发一个 审计事件 ctypes.get_last_error,不附带任何参数。

ctypes.memmove(dst, src, count)

与标准 C memmove 库函数相同:将 count 个字节从 src 拷贝到 dst*。 *dstsrc 必须为整数或可被转换为指针的 ctypes 实例。

ctypes.memset(dst, c, count)

与标准 C memset 库函数相同:将位于地址 dst 的内存块用 count 个字节的 c 值填充。 dst 必须为指定地址的整数或 ctypes 实例。

ctypes.POINTER(type)

这个工厂函数创建并返回一个新的 ctypes 指针类型。 指针类型会被缓存并在内部重用,因此重复调用此函数耗费不大。 type 必须为 ctypes 类型。

ctypes.pointer(obj)

此函数会创建一个新的指向 obj 的指针实例。 返回的对象类型为 POINTER(type(obj))

注意:如果你只是想向外部函数调用传递一个对象指针,你应当使用更为快速的 byref(obj)

ctypes.resize(obj, size)

此函数可改变 obj 的内部内存缓冲区大小,其参数必须为 ctypes 类型的实例。 没有可能将缓冲区设为小于对象类型的本机大小值,该值由 sizeof(type(obj)) 给出,但将缓冲区加大则是可能的。

ctypes.set_errno(value)

设置调用线程中系统 errno 变量的 ctypes 私有副本的当前值为 value 并返回原来的值。

引发一个 审计事件 ctypes.set_errno 附带参数 errno

ctypes.set_last_error(value)

仅限 Windows:设置调用线程中系统 LastError 变量的 ctypes 私有副本的当前值为 value 并返回原来的值。

引发一个 审计事件 ctypes.set_last_error,附带参数 error

ctypes.sizeof(obj_or_type)

返回 ctypes 类型或实例的内存缓冲区以字节表示的大小。 其功能与 C sizeof 运算符相同。

ctypes.string_at(address, size=- 1)

此函数返回从内存地址 address 开始的以字节串表示的 C 字符串。 如果指定了 size,则将其用作长度,否则将假定字符串以零值结尾。

引发一个 审计事件 ctypes.string_at,附带参数 address, size

ctypes.WinError(code=None, descr=None)

仅限 Windows:此函数可能是 ctypes 中名字起得最差的函数。 它会创建一个 OSError 的实例。 如果未指定 code,则会调用 GetLastError 来确定错误码。 如果未指定 descr,则会调用 FormatError() 来获取错误的文本描述。

在 3.3 版更改: 以前是会创建一个 WindowsError 的实例。

ctypes.wstring_at(address, size=- 1)

此函数返回从内存地址 address 开始的以字符串表示的宽字节字符串。 如果指定了 size,则将其用作字符串中的字符数量,否则将假定字符串以零值结尾。

引发一个 审计事件 ctypes.wstring_at,附带参数 address, size

数据类型

class ctypes._CData

这个非公有类是所有 ctypes 数据类型的共同基类。 另外,所有 ctypes 类型的实例都包含一个存放 C 兼容数据的内存块;该内存块的地址可由 addressof() 辅助函数返回。 还有一个实例变量被公开为 _objects;此变量包含其他在内存块包含指针的情况下需要保持存活的 Python 对象。

ctypes 数据类型的通用方法,它们都是类方法(严谨地说,它们是 metaclass 的方法):

  • from_buffer(source[, offset])

    此方法返回一个共享 source 对象缓冲区的 ctypes 实例。 source 对象必须支持可写缓冲区接口。 可选的 offset 形参指定以字节表示的源缓冲区内偏移量;默认值为零。 如果源缓冲区不够大则会引发 ValueError

    引发一个 审计事件 ctypes.cdata/buffer 附带参数 pointer, size, offset

  • from_buffer_copy(source[, offset])

    此方法创建一个 ctypes 实例,从 source 对象缓冲区拷贝缓冲区,该对象必须是可读的。 可选的 offset 形参指定以字节表示的源缓冲区内偏移量;默认值为零。 如果源缓冲区不够大则会引发 ValueError

    引发一个 审计事件 ctypes.cdata/buffer 附带参数 pointer, size, offset

  • from_address(address)

    此方法会使用 address 所指定的内存返回一个 ctypes 类型的实例,该参数必须为一个整数。

    引发一个 审计事件 ctypes.cdata,附带参数 address

  • from_param(obj)

    此方法会将 obj 适配为一个 ctypes 类型。 它调用时会在当该类型存在于外部函数的 argtypes 元组时传入外部函数调用所使用的实际对象;它必须返回一个可被用作函数调用参数的对象。

    所有 ctypes 数据类型都带有这个类方法的默认实现,它通常会返回 obj,如果该对象是此类型的实例的话。 某些类型也能接受其他对象。

  • in_dll(library, name)

    此方法返回一个由共享库导出的 ctypes 类型。 name 为导出数据的符号名称,library 为所加载的共享库。

ctypes 数据类型的通用实例变量:

  • _b_base_

    有时 ctypes 数据实例并不拥有它们所包含的内存块,它们只是共享了某个基对象的部分内存块。 _b_base_ 只读成员是拥有内存块的根 ctypes 对象。

  • _b_needsfree_

    这个只读变量在 ctypes 数据实例自身已分配了内存块时为真值,否则为假值。

  • _objects

    这个成员或者为 None,或者为一个包含需要保持存活以使内存块的内存保持有效的 Python 对象的字典。 这个对象只是出于调试目的而对外公开;绝对不要修改此字典的内容。

基础数据类型

class ctypes._SimpleCData

这个非公有类是所有基本 ctypes 数据类型的基类。 它在这里被提及是因为它包含基本 ctypes 数据类型共有的属性。 _SimpleCData_CData 的子类,因此继承了其方法和属性。 非指针及不包含指针的 ctypes 数据类型现在将可以被封存。

实例拥有一个属性:

  • value

    这个属性包含实例的实际值。 对于整数和指针类型,它是一个整数,对于字符类型,它是一个单字符字符串对象或字符串,对于字符指针类型,它是一个 Python 字节串对象或字符串。

    当从 ctypes 实例提取 value 属性时,通常每次会返回一个新的对象。 ctypes没有 实现原始对象返回,它总是会构造一个新的对象。 所有其他 ctypes 对象实例也同样如此。

基本数据类型当作为外部函数调用结果被返回或者作为结构字段成员或数组项被提取时,会透明地转换为原生 Python 类型。 换句话说,如果某个外部函数具有 c_char_prestype,你将总是得到一个 Python 字节串对象,而 不是 一个 c_char_p 实例。

基本数据类型的子类并 没有 继续此行为。 因此,如果一个外部函数的 restypec_void_p 的一个子类,你将从函数调用得到一个该子类的实例。 当然,你可以通过访问 value 属性来获取指针的值。

这些是基本 ctypes 数据类型:

class ctypes.c_byte

代表 C signed char 数据类型,并将值解读为一个小整数。 该构造器接受一个可选的整数初始化器;不会执行溢出检查。

class ctypes.c_char

代表 C char 数据类型,并将值解读为单个字符。 该构造器接受一个可选的字符串初始化器,字符串的长度必须恰好为一个字符。

class ctypes.c_char_p

当指向一个以零为结束符的字符串时代表 C char* 数据类型。 对于通用字符指针来说也可能指向二进制数据,必须要使用 POINTER(c_char)。 该构造器接受一个整数地址,或者一个字节串对象。

class ctypes.c_double

代表 C double 数据类型。 该构造器接受一个可选的浮点数初始化器。

class ctypes.c_longdouble

代表 C long double 数据类型。 该构造器接受一个可选的浮点数初始化器。 在 sizeof(long double) == sizeof(double) 的平台上它是 c_double 的一个别名。

class ctypes.c_float

代表 C float 数据类型。 该构造器接受一个可选的浮点数初始化器。

class ctypes.c_int

代表 C signed int 数据类型。 该构造器接受一个可选的整数初始化器;不会执行溢出检查。 在 sizeof(int) == sizeof(long) 的平台上它是 c_long 的一个别名。

class ctypes.c_int8

代表 C 8 位 signed int 数据类型。 通常是 c_byte 的一个别名。

class ctypes.c_int16

代表 C 16 位 signed int 数据类型。 通常是 c_short 的一个别名。

class ctypes.c_int32

代表 C 32 位 signed int 数据类型。 通常是 c_int 的一个别名。

class ctypes.c_int64

代表 C 64 位 signed int 数据类型。 通常是 c_longlong 的一个别名。

class ctypes.c_long

代表 C signed long 数据类型。 该构造器接受一个可选的整数初始化器;不会执行溢出检查。

class ctypes.c_longlong

代表 C signed long long 数据类型。 该构造器接受一个可选的整数初始化器;不会执行溢出检查。

class ctypes.c_short

代表 C signed short 数据类型。 该构造器接受一个可选的整数初始化器;不会执行溢出检查。

class ctypes.c_size_t

代表 C size_t 数据类型。

class ctypes.c_ssize_t

代表 C ssize_t 数据类型。

3.2 新版功能.

class ctypes.c_ubyte

代表 C unsigned char 数据类型,它将值解读为一个小整数。 该构造器接受一个可选的整数初始化器;不会执行溢出检查。

class ctypes.c_uint

代表 C unsigned int 数据类型。 该构造器接受一个可选的整数初始化器;不会执行溢出检查。 在 sizeof(int) == sizeof(long) 的平台上它是 c_ulong 的一个别名。

class ctypes.c_uint8

代表 C 8 位 unsigned int 数据类型。 通常是 c_ubyte 的一个别名。

class ctypes.c_uint16

代表 C 16 位 unsigned int 数据类型。 通常是 c_ushort 的一个别名。

class ctypes.c_uint32

代表 C 32 位 unsigned int 数据类型。 通常是 c_uint 的一个别名。

class ctypes.c_uint64

代表 C 64 位 unsigned int 数据类型。 通常是 c_ulonglong 的一个别名。

class ctypes.c_ulong

代表 C unsigned long 数据类型。 该构造器接受一个可选的整数初始化器;不会执行溢出检查。

class ctypes.c_ulonglong

代表 C unsigned long long 数据类型。 该构造器接受一个可选的整数初始化器;不会执行溢出检查。

class ctypes.c_ushort

代表 C unsigned short 数据类型。 该构造器接受一个可选的整数初始化器;不会执行溢出检查。

class ctypes.c_void_p

代表 C void* 类型。 该值被表示为整数形式。 该构造器接受一个可选的整数初始化器。

class ctypes.c_wchar

代表 C wchar_t 数据类型,并将值解读为一单个字符的 unicode 字符串。 该构造器接受一个可选的字符串初始化器,字符串的长度必须恰好为一个字符。

class ctypes.c_wchar_p

代表 C wchar_t* 数据类型,它必须为指向以零为结束符的宽字符串的指针。 该构造器接受一个整数地址或者一个字符串。

class ctypes.c_bool

代表 C bool 数据类型 (更准确地说是 C99 _Bool)。 它的值可以为 TrueFalse,并且该构造器接受任何具有逻辑值的对象。

class ctypes.HRESULT

Windows 专属:代表一个 HRESULT 值,它包含某个函数或方法调用的成功或错误信息。

class ctypes.py_object

代表 C PyObject* 数据类型。 不带参数地调用此构造器将创建一个 NULL PyObject* 指针。

ctypes.wintypes 模块提供了其他许多 Windows 专属的数据类型,例如 HWND, WPARAMDWORD。 还定义了一些有用的结构体例如 MSGRECT

结构化数据类型

class ctypes.Union(\args, *kw)

本机字节序的联合所对应的抽象基类。

class ctypes.BigEndianStructure(\args, *kw)

大端 字节序的结构体所对应的抽象基类。

class ctypes.LittleEndianStructure(\args, *kw)

小端 字节序的结构体所对应的抽象基类。

非本机字节序的结构体不能包含指针类型字段,或任何其他包含指针类型字段的数据类型。

class ctypes.Structure(\args, *kw)

本机 字节序的结构体所对应的抽象基类。

实际的结构体和联合类型必须通过子类化这些类型之一来创建,并且至少要定义一个 _fields_ 类变量。 ctypes 将创建 descriptor,它允许通过直接属性访问来读取和写入字段。 这些是

  • _fields_

    一个定义结构体字段的序列。 其中的条目必须为 2 元组或 3 元组。 元组的第一项是字段名称,第二项指明字段类型;它可以是任何 ctypes 数据类型。

    对于整数类型字段例如 c_int,可以给定第三个可选项。 它必须是一个定义字段比特位宽度的小正整数。

    字段名称在一个结构体或联合中必须唯一。 不会检查这个唯一性,但当名称出现重复时将只有一个字段可被访问。

    可以在定义 Structure 子类的类语句 之后 再定义 _fields_ 类变量,这将允许创建直接或间接引用其自身的数据类型:

    class List(Structure):
        pass
    List._fields_ = [("pnext", POINTER(List)),
                     ...
                    ]

    但是,_fields_ 类变量必须在类型第一次被使用(创建实例,调用 sizeof() 等等)之前进行定义。 在此之后对 _fields_ 类变量赋值将会引发 AttributeError。

    可以定义结构体类型的子类,它们会继承基类的字段再加上在子类中定义的任何 _fields_

  • _pack_

    一个可选的小整数,它允许覆盖实体中结构体字段的对齐方式。 当 _fields_ 被赋值时必须已经定义了 _pack_,否则它将没有效果。

  • _anonymous_

    一个可选的序列,它会列出未命名(匿名)字段的名称。 当 _fields_ 被赋值时必须已经定义了 _anonymous_,否则它将没有效果。

    在此变量中列出的字段必须为结构体或联合类型字段。 ctypes 将在结构体类型中创建描述器以允许直接访问嵌套字段,而无需创建对应的结构体或联合字段。

    以下是一个示例类型(Windows):

    class _U(Union):
        _fields_ = [("lptdesc", POINTER(TYPEDESC)),
                    ("lpadesc", POINTER(ARRAYDESC)),
                    ("hreftype", HREFTYPE)]
    class TYPEDESC(Structure):
        _anonymous_ = ("u",)
        _fields_ = [("u", _U),
                    ("vt", VARTYPE)]

    TYPEDESC 结构体描述了一个 COM 数据类型,vt 字段指明哪个联合字段是有效的。 由于 u 字段被定义为匿名字段,现在可以直接从 TYPEDESC 实例访问成员。 td.lptdesctd.u.lptdesc 是等价的,但前者速度更快,因为它不需要创建临时的联合实例:

    td = TYPEDESC()
    td.vt = VT_PTR
    td.lptdesc = POINTER(some_type)
    td.u.lptdesc = POINTER(some_type)

可以定义结构体的子类,它们会继承基类的字段。 如果子类定义具有单独的 _fields_ 变量,在其中指定的字段会被添加到基类的字段中。

结构体和联合的构造器均可接受位置和关键字参数。 位置参数用于按照 _fields_ 中的出现顺序来初始化成员字段。 构造器中的关键字参数会被解读为属性赋值,因此它们将以相应的名称来初始化 _fields_,或为不存在于 _fields_ 中的名称创建新的属性。

数组与指针

class ctypes.Array(\args*)

数组的抽象基类。

创建实际数组类型的推荐方式是通过将任意 ctypes 类型与一个正整数相乘。 作为替代方式,你也可以子类化这个类型并定义 _length__type_ 类变量。 数组元素可使用标准的抽取和切片方式来读写;对于切片读取,结果对象本身 并非 一个 Array

  • _length_

    一个指明数组中元素数量的正整数。 超出范围的抽取会导致 IndexError。 该值将由 len() 返回。

  • _type_

    指明数组中每个元素的类型。

Array 子类构造器可接受位置参数,用来按顺序初始化元素。

class ctypes._Pointer

私有对象,指针的抽象基类。

实际的指针类型是通过调用 POINTER() 并附带其将指向的类型来创建的;这会由 pointer() 自动完成。

如果一个指针指向的是数组,则其元素可使用标准的抽取和切片方式来读写。 指针对象没有长度,因此 len() 将引发 TypeError。 抽取负值将会从指针 之前 的内存中读取(与 C 一样),并且超出范围的抽取将可能因非法访问而导致崩溃(视你的运气而定)。

  • _type_

    指明所指向的类型。

  • contents

    返回指针所指向的对象。 对此属性赋值会使指针改为指向所赋值的对象。


文章作者: 杰克成
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 杰克成 !
评论
  目录