printf 风格的字节串格式化

备注

此处介绍的格式化操作具有多种怪异特性,可能导致许多常见错误(例如无法正确显示元组和字典)。 如果要打印的值可能为元组或字典,请将其放入一个元组中。

字节串对象 (bytes/bytearray) 具有一种特殊的内置操作:使用 % (取模) 运算符。 这也被称为字节串的 格式化插值 运算符。 对于 format % values (其中 format 为一个字节串对象),在 format 中的 % 转换标记符将被替换为零个或多个 values 条目。 其效果类似于在 C 语言中使用 sprintf()

如果 format 要求一个单独参数,则 values 可以为一个非元组对象。 [5] 否则的话,values 必须或是是一个包含项数与格式字节串对象中指定的转换符项数相同的元组,或者是一个单独的映射对象(例如元组)。

转换标记符包含两个或更多字符并具有以下组成,且必须遵循此处规定的顺序:

  • '%' 字符,用于标记转换符的起始。

  • 映射键(可选),由加圆括号的字符序列组成 (例如 (somename))。

  • 转换旗标(可选),用于影响某些转换类型的结果。

  • 最小字段宽度(可选)。 如果指定为 '*' (星号),则实际宽度会从 values 元组的下一元素中读取,要转换的对象则为最小字段宽度和可选的精度之后的元素。

  • 精度(可选),以在 '.' (点号) 之后加精度值的形式给出。 如果指定为 '*' (星号),则实际精度会从 values 元组的下一元素中读取,要转换的对象则为精度之后的元素。

  • 长度修饰符(可选)。

  • 转换类型。

当右边的参数为一个字典(或其他映射类型)时,字节串对象中的格式 必须 包含加圆括号的映射键,对应 '%' 字符之后字典中的每一项。 映射键将从映射中选取要格式化的值。 例如:

  1. >>> print(b'%(language)s has %(number)03d quote types.' %
  2. ... {b'language': b"Python", b"number": 2})
  3. b'Python has 002 quote types.'

在此情况下格式中不能出现 * 标记符(因其需要一个序列类的参数列表)。

转换旗标为:

旗标 含意
'#' 值的转换将使用“替代形式”(具体定义见下文)。
'0' 转换将为数字值填充零字符。
'-' 转换值将靠左对齐(如果同时给出 '0' 转换,则会覆盖后者)。
' ' (空格) 符号位转换产生的正数(或空字符串)前将留出一个空格。
'+' 符号字符 ('+''-') 将显示于转换结果的开头(会覆盖 "空格" 旗标)。

可以给出长度修饰符 (h, lL),但会被忽略,因为对 Python 来说没有必要 — 所以 %ld 等价于 %d

转换类型为:

转换符 含意 备注
'd' 有符号十进制整数。
'i' 有符号十进制整数。
'o' 有符号八进制数。 (1)
'u' 过时类型 — 等价于 'd' (8)
'x' 有符号十六进制数(小写)。 (2)
'X' 有符号十六进制数(大写)。 (2)
'e' 浮点指数格式(小写)。 (3)
'E' 浮点指数格式(大写)。 (3)
'f' 浮点十进制格式。 (3)
'F' 浮点十进制格式。 (3)
'g' 浮点格式。 如果指数小于 -4 或不小于精度则使用小写指数格式,否则使用十进制格式。 (4)
'G' 浮点格式。 如果指数小于 -4 或不小于精度则使用大写指数格式,否则使用十进制格式。 (4)
'c' 单个字节(接受整数或单个字节对象)。
'b' 字节串(任何遵循 缓冲区协议 或是具有 __bytes__() 的对象)。 (5)
's' 's''b' 的一个别名,只应当在基于 Python2/3 的代码中使用。 (6)
'a' 字节串(使用 repr(obj).encode('ascii', 'backslashreplace') 来转换任意 Python 对象)。 (5)
'r' 'r''a' 的一个别名,只应当在基于 Python2/3 的代码中使用。 (7)
'%' 不转换参数,在结果中输出一个 '%' 字符。

注释:

  • 此替代形式会在第一个数码之前插入标示八进制数的前缀 ('0o')。

  • 此替代形式会在第一个数码之前插入 '0x''0X' 前缀(取决于是使用 'x' 还是 'X' 格式)。

  • 此替代形式总是会在结果中包含一个小数点,即使其后并没有数码。

小数点后的数码位数由精度决定,默认为 6。

  • 此替代形式总是会在结果中包含一个小数点,末尾各位的零不会如其他情况下那样被移除。

小数点前后的有效数码位数由精度决定,默认为 6。

  • 如果精度为 N,输出将截短为 N 个字符。

  • b'%s' 已弃用,但在 3.x 系列中将不会被移除。

  • b'%r' 已弃用,但在 3.x 系列中将不会被移除。

  • 参见 PEP 237 [https://peps.python.org/pep-0237/]。

备注

此方法的 bytearray 版本 并非 原地操作 —— 它总是产生一个新对象,即便没有做任何改变。

参见

PEP 461 [https://peps.python.org/pep-0461/] - 为 bytes 和 bytearray 添加 % 格式化

Added in version 3.5.

内存视图

memoryview 对象允许 Python 代码访问一个对象的内部数据,只要该对象支持 缓冲区协议 而无需进行拷贝。

  • class memoryview(object)
  • 创建一个引用 object 的 memoryview 。 object 必须支持缓冲区协议。支持缓冲区协议的内置对象有 bytesbytearray

memoryview 有 元素 的概念, 元素 指由原始 object 处理的原子内存单元。对于许多简单的类型,如 bytesbytearray ,一个元素是一个字节,但其他类型,如 array.array 可能有更大的元素。

len(view) 等于 tolist 的长度,即视图的嵌套列表表示形式。 如果 view.ndim = 1,它将等于视图中元素的数量。

在 3.12 版本发生变更: 如果 view.ndim == 0,现在 len(view) 将引发 TypeError 而不是返回 1.

itemsize 属性将给出单个元素的字节数。

memoryview 支持通过切片和索引访问其元素。 一维切片的结果将是一个子视图:

  1. >>> v = memoryview(b'abcefg')
  2. >>> v[1]
  3. 98
  4. >>> v[-1]
  5. 103
  6. >>> v[1:4]
  7. <memory at 0x7f3ddc9f4350>
  8. >>> bytes(v[1:4])
  9. b'bce'

如果 format 是一个来自于 struct 模块的原生格式说明符,则也支持使用整数或由整数构成的元组进行索引,并返回具有正确类型的单个 元素。 一维内存视图可以使用一个整数或由一个整数构成的元组进行索引。 多维内存视图可以使用由恰好 ndim 个整数构成的元素进行索引,ndim 即其维度。 零维内存视图可以使用空元组进行索引。

这里是一个使用非字节格式的例子:

  1. >>> import array
  2. >>> a = array.array('l', [-11111111, 22222222, -33333333, 44444444])
  3. >>> m = memoryview(a)
  4. >>> m[0]
  5. -11111111
  6. >>> m[-1]
  7. 44444444
  8. >>> m[::2].tolist()
  9. [-11111111, -33333333]

如果下层对象是可写的,则内存视图支持一维切片赋值。 改变大小则不被允许:

  1. >>> data = bytearray(b'abcefg')
  2. >>> v = memoryview(data)
  3. >>> v.readonly
  4. False
  5. >>> v[0] = ord(b'z')
  6. >>> data
  7. bytearray(b'zbcefg')
  8. >>> v[1:4] = b'123'
  9. >>> data
  10. bytearray(b'z123fg')
  11. >>> v[2:3] = b'spam'
  12. Traceback (most recent call last):
  13. File "<stdin>", line 1, in <module>
  14. ValueError: memoryview assignment: lvalue and rvalue have different structures
  15. >>> v[2:6] = b'spam'
  16. >>> data
  17. bytearray(b'z1spam')

格式符为 'B', 'b' 或 'c' 的 hashable (只读) 类型的一维内存视图也是可哈希对象。 哈希被定义为 hash(m) == hash(m.tobytes()):

  1. >>> v = memoryview(b'abcefg')
  2. >>> hash(v) == hash(b'abcefg')
  3. True
  4. >>> hash(v[2:4]) == hash(b'ce')
  5. True
  6. >>> hash(v[::-2]) == hash(b'abcefg'[::-2])
  7. True

在 3.3 版本发生变更: 一维内存视图现在可以被切片。 格式符为 'B', 'b' 或 'c' 的一维内存视图现在是 hashable

在 3.4 版本发生变更: 内存视图现在会自动注册为 collections.abc.Sequence

在 3.5 版本发生变更: 内存视图现在可使用整数元组进行索引。

memoryview 具有以下一些方法:

  • eq(exporter)
  • memoryview 与 PEP 3118 [https://peps.python.org/pep-3118/] 中的导出器这两者如果形状相同,并且如果当使用 struct 语法解读操作数的相应格式代码时所有对应值都相同,则它们就是等价的。

对于 tolist() 当前所支持的 struct 格式字符串子集,如果 v.tolist() == w.tolist()vw 相等:

  1. >>> import array
  2. >>> a = array.array('I', [1, 2, 3, 4, 5])
  3. >>> b = array.array('d', [1.0, 2.0, 3.0, 4.0, 5.0])
  4. >>> c = array.array('b', [5, 3, 1])
  5. >>> x = memoryview(a)
  6. >>> y = memoryview(b)
  7. >>> x == a == y == b
  8. True
  9. >>> x.tolist() == a.tolist() == y.tolist() == b.tolist()
  10. True
  11. >>> z = y[::-2]
  12. >>> z == c
  13. True
  14. >>> z.tolist() == c.tolist()
  15. True

如果两边的格式字符串都不被 struct 模块所支持,则两对象比较结果总是不相等(即使格式字符串和缓冲区内容相同):

  1. >>> from ctypes import BigEndianStructure, c_long
  2. >>> class BEPoint(BigEndianStructure):
  3. ... fields = [("x", c_long), ("y", c_long)]
  4. ...
  5. >>> point = BEPoint(100, 200)
  6. >>> a = memoryview(point)
  7. >>> b = memoryview(point)
  8. >>> a == point
  9. False
  10. >>> a == b
  11. False

请注意,与浮点数的情况一样,对于内存视图对象来说,v is w 也 并不 意味着 v == w

在 3.3 版本发生变更: 之前的版本比较原始内存时会忽略条目的格式与逻辑数组结构。

  • tobytes(order='C')
  • 将缓冲区中的数据作为字节串返回。 这相当于在内存视图上调用 bytes 构造器。
  1. >>> m = memoryview(b"abc")
  2. >>> m.tobytes()
  3. b'abc'
  4. >>> bytes(m)
  5. b'abc'

对于非连续数组,结果等于平面化表示的列表,其中所有元素都转换为字节串。 tobytes() 支持所有格式字符串,不符合 struct 模块语法的那些也包括在内。

Added in version 3.8: order 可以为 {'C', 'F', 'A'}。 当 order 为 'C' 或 'F' 时,原始数组的数据会被转换至 C 或 Fortran 顺序。 对于连续视图,'A' 会返回物理内存的精确副本。 特别地,内存中的 Fortran 顺序会被保留。对于非连续视图,数据会先被转换为 C 形式。 order=None 与 order='C' 是相同的。

  • hex([sep[, bytes_per_sep]])
  • 返回一个字符串对象,其中分别以两个十六进制数码表示缓冲区里的每个字节。
  1. >>> m = memoryview(b"abc")
  2. >>> m.hex()
  3. '616263'

Added in version 3.5.

在 3.8 版本发生变更: 与 bytes.hex() 相似, memoryview.hex() 现在支持可选的 sep 和 bytes_per_sep 参数以在十六进制输出的字节之间插入分隔符。

  • tolist()
  • 将缓冲区内的数据以一个元素列表的形式返回。
  1. >>> memoryview(b'abc').tolist()
  2. [97, 98, 99]
  3. >>> import array
  4. >>> a = array.array('d', [1.1, 2.2, 3.3])
  5. >>> m = memoryview(a)
  6. >>> m.tolist()
  7. [1.1, 2.2, 3.3]

在 3.3 版本发生变更: tolist() 现在支持 struct 模块语法中的所有单字符原生格式以及多维表示形式。

  • toreadonly()
  • 返回 memoryview 对象的只读版本。 原始的 memoryview 对象不会被改变。
  1. >>> m = memoryview(bytearray(b'abc'))
  2. >>> mm = m.toreadonly()
  3. >>> mm.tolist()
  4. [97, 98, 99]
  5. >>> mm[0] = 42
  6. Traceback (most recent call last):
  7. File "<stdin>", line 1, in <module>
  8. TypeError: cannot modify readonly memory
  9. >>> m[0] = 43
  10. >>> mm.tolist()
  11. [43, 98, 99]

Added in version 3.8.

  • release()
  • 释放由内存视图对象所公开的底层缓冲区。 许多对象在被视图所获取时都会采取特殊动作(例如,bytearray 将会暂时禁止调整大小);因此,调用 release() 可以方便地尽早去除这些限制(并释放任何多余的资源)。

在此方法被调用后,任何对该视图的进一步操作都将引发 ValueError (除了可被多次调用的 release() 本身):

  1. >>> m = memoryview(b'abc')
  2. >>> m.release()
  3. >>> m[0]
  4. Traceback (most recent call last):
  5. File "<stdin>", line 1, in <module>
  6. ValueError: operation forbidden on released memoryview object

使用 with 语句,可以通过上下文管理协议达到类似的效果:

  1. >>> with memoryview(b'abc') as m:
  2. ... m[0]
  3. ...
  4. 97
  5. >>> m[0]
  6. Traceback (most recent call last):
  7. File "<stdin>", line 1, in <module>
  8. ValueError: operation forbidden on released memoryview object

Added in version 3.2.

  • cast(format[, shape])
  • 将内存视图转化为新的格式或形状。 shape 默认为 [byte_length//new_itemsize],这意味着结果视图将是一维的。 返回值是一个新的内存视图,但缓冲区本身不会被复制。 支持的转化有 1D -> C-contiguous 和 C-contiguous -> 1D。

目标格式被限制为 struct 语法中的单一元素的原生格式。 这些格式中的一种必须为字节格式 ('B', 'b' 或 'c')。 结果的字节长度必须与原始长度相同。 请注意全部字节长度可能取决于具体操作系统。

将 1D/long 转换为 1D/unsigned bytes:

  1. >>> import array
  2. >>> a = array.array('l', [1,2,3])
  3. >>> x = memoryview(a)
  4. >>> x.format
  5. 'l'
  6. >>> x.itemsize
  7. 8
  8. >>> len(x)
  9. 3
  10. >>> x.nbytes
  11. 24
  12. >>> y = x.cast('B')
  13. >>> y.format
  14. 'B'
  15. >>> y.itemsize
  16. 1
  17. >>> len(y)
  18. 24
  19. >>> y.nbytes
  20. 24

将 1D/unsigned bytes 转换为 1D/char:

  1. >>> b = bytearray(b'zyz')
  2. >>> x = memoryview(b)
  3. >>> x[0] = b'a'
  4. Traceback (most recent call last): ...
  5. TypeError: memoryview: invalid type for format 'B'
  6. >>> y = x.cast('c')
  7. >>> y[0] = b'a'
  8. >>> b
  9. bytearray(b'ayz')

将 1D/bytes 转换为 3D/ints 再转换为 1D/signed char:

  1. >>> import struct
  2. >>> buf = struct.pack("i"*12, *list(range(12)))
  3. >>> x = memoryview(buf)
  4. >>> y = x.cast('i', shape=[2,2,3])
  5. >>> y.tolist()
  6. [[[0, 1, 2], [3, 4, 5]], [[6, 7, 8], [9, 10, 11]]]
  7. >>> y.format
  8. 'i'
  9. >>> y.itemsize
  10. 4
  11. >>> len(y)
  12. 2
  13. >>> y.nbytes
  14. 48
  15. >>> z = y.cast('b')
  16. >>> z.format
  17. 'b'
  18. >>> z.itemsize
  19. 1
  20. >>> len(z)
  21. 48
  22. >>> z.nbytes
  23. 48

将 1D/unsigned long 转换为 2D/unsigned long:

  1. >>> buf = struct.pack("L"*6, *list(range(6)))
  2. >>> x = memoryview(buf)
  3. >>> y = x.cast('L', shape=[2,3])
  4. >>> len(y)
  5. 2
  6. >>> y.nbytes
  7. 48
  8. >>> y.tolist()
  9. [[0, 1, 2], [3, 4, 5]]

Added in version 3.3.

在 3.5 版本发生变更: 当转换为字节视图时,源格式将不再受限。

还存在一些可用的只读属性:

  • obj
  • 内存视图的下层对象:
  1. >>> b = bytearray(b'xyz')
  2. >>> m = memoryview(b)
  3. >>> m.obj is b
  4. True

Added in version 3.3.

  • nbytes
  • nbytes == product(shape) * itemsize == len(m.tobytes())。 这是数组在连续表示时将会占用的空间总字节数。 它不一定等于 len(m):
  1. >>> import array
  2. >>> a = array.array('i', [1,2,3,4,5])
  3. >>> m = memoryview(a)
  4. >>> len(m)
  5. 5
  6. >>> m.nbytes
  7. 20
  8. >>> y = m[::2]
  9. >>> len(y)
  10. 3
  11. >>> y.nbytes
  12. 12
  13. >>> len(y.tobytes())
  14. 12

多维数组:

  1. >>> import struct
  2. >>> buf = struct.pack("d"*12, *[1.5*x for x in range(12)])
  3. >>> x = memoryview(buf)
  4. >>> y = x.cast('d', shape=[3,4])
  5. >>> y.tolist()
  6. [[0.0, 1.5, 3.0, 4.5], [6.0, 7.5, 9.0, 10.5], [12.0, 13.5, 15.0, 16.5]]
  7. >>> len(y)
  8. 3
  9. >>> y.nbytes
  10. 96

Added in version 3.3.

  • readonly
  • 一个表明内存是否只读的布尔值。

  • format

  • 一个字符串,包含视图中每个元素的格式(表示为 struct 模块样式)。 内存视图可以从具有任意格式字符串的导出器创建,但某些方法 (例如 tolist()) 仅限于原生的单元素格式。

在 3.3 版本发生变更: 格式 'B' 现在会按照 struct 模块语法来处理。 这意味着 memoryview(b'abc')[0] == b'abc'[0] == 97

  • itemsize
  • memoryview 中每个元素以字节表示的大小:
  1. >>> import array, struct
  2. >>> m = memoryview(array.array('H', [32000, 32001, 32002]))
  3. >>> m.itemsize
  4. 2
  5. >>> m[0]
  6. 32000
  7. >>> struct.calcsize('H') == m.itemsize
  8. True
  • ndim
  • 一个整数,表示内存所代表的多维数组具有多少个维度。

  • shape

  • 一个整数元组,通过 ndim 的长度值给出内存所代表的 N 维数组的形状。

在 3.3 版本发生变更: 当 ndim = 0 时值为空元组而不再为 None

  • strides
  • 一个整数元组,通过 ndim 的长度给出以字节表示的大小,以便访问数组中每个维度上的每个元素。

在 3.3 版本发生变更: 当 ndim = 0 时值为空元组而不再为 None

  • suboffsets
  • 供 PIL 风格的数组内部使用。 该值仅作为参考信息。

  • c_contiguous

  • 一个表明内存是否为 C-contiguous 的布尔值。

Added in version 3.3.

  • f_contiguous
  • 一个表明内存是否为 Fortran contiguous 的布尔值。

Added in version 3.3.

  • contiguous
  • 一个表明内存是否为 contiguous 的布尔值。

Added in version 3.3.

集合类型 —- set, frozenset

set 对象是由具有唯一性的 hashable 对象所组成的无序多项集。 常见的用途包括成员检测、从序列中去除重复项以及数学中的集合类计算,例如交集、并集、差集与对称差集等等。 (关于其他容器对象请参看 dict, listtuple 等内置类,以及 collections 模块。)

与其他多项集一样,集合也支持 x in set, len(set)for x in set。 作为一种无序的多项集,集合并不记录元素位置或插入顺序。 相应地,集合不支持索引、切片或其他序列类的操作。

目前有两种内置集合类型,setfrozensetset 类型是可变的 —- 其内容可以使用 add()remove() 这样的方法来改变。 由于是可变类型,它没有哈希值,且不能被用作字典的键或其他集合的元素。 frozenset 类型是不可变并且为 hashable —- 其内容在被创建后不能再改变;因此它可以被用作字典的键或其他集合的元素。

除了可以使用 set 构造器,非空的 set (不是 frozenset) 还可以通过将以逗号分隔的元素列表包含于花括号之内来创建,例如: {'jack', 'sjoerd'}

两个类的构造器具有相同的作用方式:

  • class set([iterable])
  • class frozenset([iterable])
  • 返回一个新的 set 或 frozenset 对象,其元素来自于 iterable。 集合的元素必须为 hashable。 要表示由集合对象构成的集合,所有的内层集合必须为 frozenset 对象。 如果未指定 iterable,则将返回一个新的空集合。

集合可用多种方式来创建:

  • 使用花括号内以逗号分隔元素的方式: {'jack', 'sjoerd'}

  • 使用集合推导式: {c for c in 'abracadabra' if c not in 'abc'}

  • 使用类型构造器: set(), set('foobar'), set(['a', 'b', 'foo'])

setfrozenset 的实例提供以下操作:

  • len(s)
  • 返回集合 s 中的元素数量(即 s 的基数)。

  • x in s

  • 检测 x 是否为 s 中的成员。

  • x not in s

  • 检测 x 是否非 s 中的成员。

  • isdisjoint(other)

  • 如果集合中没有与 other 共有的元素则返回 True。 当且仅当两个集合的交集为空集合时,两者为不相交集合。

  • issubset(other)

  • set <= other
  • 检测是否集合中的每个元素都在 other 之中。

  • set < other

  • 检测集合是否为 other 的真子集,即 set <= other and set != other

  • issuperset(other)

  • set >= other
  • 检测是否 other 中的每个元素都在集合之中。

  • set > other

  • 检测集合是否为 other 的真超集,即 set >= other and set != other

  • union(*others)

  • set | other | …
  • 返回一个新集合,其中包含来自原集合以及 others 指定的所有集合中的元素。

  • intersection(*others)

  • set & other & …
  • 返回一个新集合,其中包含原集合以及 others 指定的所有集合中共有的元素。

  • difference(*others)

  • set - other - …
  • 返回一个新集合,其中包含原集合中在 others 指定的其他集合中不存在的元素。

  • symmetric_difference(other)

  • set ^ other
  • 返回一个新集合,其中的元素或属于原集合或属于 other 指定的其他集合,但不能同时属于两者。

  • copy()

  • 返回原集合的浅拷贝。

注意, union()intersection()difference()symmetric_difference()issubset()issuperset() 方法的非运算符版本可以接受任何可迭代对象作为一个参数。相比之下,基于运算符的对应方法则要求参数为集合对象。这就避开了像 set('abc') & 'cbs' 这样容易出错的结构,而换成了可读性更好的 set('abc').intersection('cbs')

setfrozenset 均支持集合与集合的比较。 两个集合当且仅当每个集合中的每个元素均包含于另一个集合之内(即各为对方的子集)时则相等。 一个集合当且仅当其为另一个集合的真子集(即为后者的子集但两者不相等)时则小于另一个集合。 一个集合当且仅当其为另一个集合的真超集(即为后者的超集但两者不相等)时则大于另一个集合。

set 的实例与 frozenset 的实例之间基于它们的成员进行比较。 例如 set('abc') == frozenset('abc') 返回 Trueset('abc') in set([frozenset('abc')]) 也一样。

子集与相等比较并不能推广为完全排序函数。 例如,任意两个非空且不相交的集合不相等且互不为对方的子集,因此以下 所有 比较均返回 False: a, a==b, or a>b

由于集合仅定义了部分排序(子集关系),因此由集合构成的列表 list.sort() 方法的输出并无定义。

集合的元素,与字典的键类似,必须为 hashable

混合了 set 实例与 frozenset 的二进制位运算将返回与第一个操作数相同的类型。例如: frozenset('ab') | set('bc') 将返回 frozenset 的实例。

下表列出了可用于 set 而不能用于不可变的 frozenset 实例的操作:

  • update(*others)
  • set |= other | …
  • 更新集合,添加来自 others 中的所有元素。

  • intersection_update(*others)

  • set &= other & …
  • 更新集合,只保留其中在所有 others 中也存在的元素。

  • difference_update(*others)

  • set -= other | …
  • 更新集合,移除其中也存在于 others 中的元素。

  • symmetric_difference_update(other)

  • set ^= other
  • 更新集合,只保留存在于集合的一方而非共同存在的元素。

  • add(elem)

  • 将元素 elem 添加到集合中。

  • remove(elem)

  • 从集合中移除元素 elem。 如果 elem 不存在于集合中则会引发 KeyError

  • discard(elem)

  • 如果元素 elem 存在于集合中则将其移除。

  • pop()

  • 从集合中移除并返回任意一个元素。 如果集合为空则会引发 KeyError

  • clear()

  • 从集合中移除所有元素。

请注意,非运算符版本的 update(), intersection_update(), difference_update()symmetric_difference_update() 方法将接受任意可迭代对象作为参数。

请注意,__contains__(), remove()discard() 方法的 elem 参数可以是一个集合。 为支持搜索等价的冻结集合,将根据 elem 临时创建一个相应的对象。

映射类型 —- dict

mapping 对象会将 hashable 值映射到任意对象。 映射属于可变对象。 目前仅有一种标准映射类型 字典。 (关于其他容器对象请参看 list, settuple 等内置类,以及 collections 模块。)

字典的键 几乎 可以为任何值。 不是 hashable 的值,即包含列表、字典或其他可变类型(按值比较而非按对象标识比较)的值不可被用作键。 比较结果相等的值(如 1, 1.0True 等)可被互换使用以索引同一个字典条目。

  • class dict(**kwargs)
  • class dict(mapping, **kwargs)
  • class dict(iterable, **kwargs)
  • 返回一个新的字典,基于可选的位置参数和可能为空的关键字参数集来初始化。

字典可用多种方式来创建:

  • 使用花括号内以逗号分隔 键: 值 对的方式: {'jack': 4098, 'sjoerd': 4127} or {4098: 'jack', 4127: 'sjoerd'}

  • 使用字典推导式: {}, {x: x ** 2 for x in range(10)}

  • 使用类型构造器: dict(), dict([('foo', 100), ('bar', 200)]), dict(foo=100, bar=200)

如果没有给出位置参数,将创建一个空字典。 如果给出一个位置参数并且其定义了 keys() 方法,则通过在该参数上调用 __getitem__() 创建一个字典并包含从该方法返回的每个键。 在其他情况下,位置参数必须是一个 iterable 对象。 该可迭代对象中的每一项本身必须是一个恰好包含两个元素的可迭代对象。 每一项中的第一个元素将成为新字典的一个键,第二个元素将成为其对应的值。 如果一个键出现多次,该键的最后一个值将成为其在新字典中的对应值。

如果给出了关键字参数,则关键字参数及其值会被加入到基于位置参数创建的字典。 如果要加入的键已存在,来自关键字参数的值将替代来自位置参数的值。

作为演示,以下示例返回的字典均等于 {"one": 1, "two": 2, "three": 3}:

  1. >>> a = dict(one=1, two=2, three=3)
  2. >>> b = {'one': 1, 'two': 2, 'three': 3}
  3. >>> c = dict(zip(['one', 'two', 'three'], [1, 2, 3]))
  4. >>> d = dict([('two', 2), ('one', 1), ('three', 3)])
  5. >>> e = dict({'three': 3, 'one': 1, 'two': 2})
  6. >>> f = dict({'one': 1, 'three': 3}, two=2)
  7. >>> a == b == c == d == e == f
  8. True

像第一个例子那样提供关键字参数的方式只能使用有效的 Python 标识符作为键。 其他方式则可使用任何有效的键。

这些是字典所支持的操作(因而自定义的映射类型也应当支持):

  • list(d)
  • 返回字典 d 中使用的所有键的列表。

  • len(d)

  • 返回字典 d 中的项数。

  • d[key]

  • 返回 d 中以 key 为键的项。 如果映射中不存在 key 则会引发 KeyError

如果字典的子类定义了方法 __missing__() 并且 key 不存在,则 d[key] 操作将调用该方法并附带键 key 作为参数。 d[key] 随后将返回或引发 __missing__(key) 调用所返回或引发的任何对象或异常。 没有其他操作或方法会唤起 __missing__()。 如果未定义 __missing__(),则会引发 KeyError__missing__() 必须是一个方法;它不能是一个实例变量:

  1. >>> class Counter(dict):
  2. ... def __missing__(self, key):
  3. ... return 0
  4. ...
  5. >>> c = Counter()
  6. >>> c['red']
  7. 0
  8. >>> c['red'] += 1
  9. >>> c['red']
  10. 1

上面的例子显示了 collections.Counter 实现的部分代码。 还有另一个不同的 __missing__ 方法是由 collections.defaultdict 所使用的。

  • d[key] = value
  • d[key] 设为 value。

  • del d[key]

  • d[key] 从 d 中移除。 如果映射中不存在 key 则会引发 KeyError

  • key in d

  • 如果 d 中存在键 key 则返回 True,否则返回 False

  • key not in d

  • 等价于 not key in d

  • iter(d)

  • 返回以字典的键为元素的迭代器。 这是 iter(d.keys()) 的快捷方式。

  • clear()

  • 移除字典中的所有元素。

  • copy()

  • 返回原字典的浅拷贝。

  • classmethod fromkeys(iterable, value=None, /)

  • 使用来自 iterable 的键创建一个新字典,并将键值设为 value。

fromkeys() 是一个返回新字典的类方法。 value 默认为 None。 所有值都只引用一个单独的实例,因此让 value 成为一个可变对象例如空列表通常是没有意义的。 要获取不同的值,请改用 字典推导式

  • get(key, default=None)
  • 如果 key 存在于字典中则返回 key 的值,否则返回 default。 如果 default 未给出则默认为 None,因而此方法绝不会引发 KeyError

  • items()

  • 返回由字典项 ((键, 值) 对) 组成的一个新视图。 参见 视图对象文档

  • keys()

  • 返回由字典键组成的一个新视图。 参见 视图对象文档

  • pop(key[, default])

  • 如果 key 存在于字典中则将其移除并返回其值,否则返回 default。 如果 default 未给出且 key 不存在于字典中,则会引发 KeyError

  • popitem()

  • 从字典中移除并返回一个 (键, 值) 对。 键值对会按 LIFO 的顺序被返回。

popitem() 适用于对字典进行消耗性的迭代,这在集合算法中经常被使用。 如果字典为空,调用 popitem() 将引发 KeyError

在 3.7 版本发生变更: 现在会确保采用 LIFO 顺序。 在之前的版本中,popitem() 会返回一个任意的键/值对。

  • reversed(d)
  • 返回一个逆序获取字典键的迭代器。 这是 reversed(d.keys()) 的快捷方式。

Added in version 3.8.

  • setdefault(key, default=None)
  • 如果字典存在键 key ,返回它的值。如果不存在,插入值为 default 的键 key ,并返回 default 。 default 默认为 None

  • update([other])

  • 使用来自 other 的键/值对更新字典,覆盖原有的键。 返回 None

update() 接受另一个具有 keys() 方法的对象(在此情况下 __getitem__() 将被调用并附带从该方法返回的键)或一个包含键/值对(以长度为二的元组或其他可迭代对象表示)的可迭代对象。 如果指定了关键字参数,则会以其所对应的键/值对更新字典: d.update(red=1, blue=2)

两个 dict.values() 视图之间的相等性比较将总是返回 False。 这在 dict.values() 与其自身比较时也同样适用:

  1. >>> d = {'a': 1}
  2. >>> d.values() == d.values()
  3. False
  • d | other
  • 合并 d 和 other 中的键和值来创建一个新的字典,两者必须都是字典。当 d 和 other 有相同键时, other 的值优先。

Added in version 3.9.

  • d |= other
  • 用 other 的键和值更新字典 d ,other 可以是 mappingiterable 的键值对。当 d 和 other 有相同键时, other 的值优先。

Added in version 3.9.

两个字典的比较当且仅当它们具有相同的 (键, 值) 对时才会相等(不考虑顺序)。 排序比较 ('<', '<=', '>=', '>') 会引发 TypeError

字典会保留插入时的顺序。 请注意对键的更新不会影响顺序。 删除并再次添加的键将被插入到末尾。

  1. >>> d = {"one": 1, "two": 2, "three": 3, "four": 4}
  2. >>> d
  3. {'one': 1, 'two': 2, 'three': 3, 'four': 4}
  4. >>> list(d)
  5. ['one', 'two', 'three', 'four']
  6. >>> list(d.values())
  7. [1, 2, 3, 4]
  8. >>> d["one"] = 42
  9. >>> d
  10. {'one': 42, 'two': 2, 'three': 3, 'four': 4}
  11. >>> del d["two"]
  12. >>> d["two"] = None
  13. >>> d
  14. {'one': 42, 'three': 3, 'four': 4, 'two': None}

在 3.7 版本发生变更: 字典顺序会确保为插入顺序。 此行为是自 3.6 版开始的 CPython 实现细节。

字典和字典视图都是可逆的。

  1. >>> d = {"one": 1, "two": 2, "three": 3, "four": 4}
  2. >>> d
  3. {'one': 1, 'two': 2, 'three': 3, 'four': 4}
  4. >>> list(reversed(d))
  5. ['four', 'three', 'two', 'one']
  6. >>> list(reversed(d.values()))
  7. [4, 3, 2, 1]
  8. >>> list(reversed(d.items()))
  9. [('four', 4), ('three', 3), ('two', 2), ('one', 1)]

在 3.8 版本发生变更: 字典现在是可逆的。

参见

types.MappingProxyType 可被用来创建一个 dict 的只读视图。

字典视图对象

dict.keys(), dict.values()dict.items() 所返回的对象是 视图对象。 该对象提供字典条目的一个动态视图,这意味着当字典改变时,视图也会相应改变。

字典视图可以被迭代以产生与其对应的数据,并支持成员检测:

  • len(dictview)
  • 返回字典中的条目数。
  • iter(dictview)
  • 返回字典中的键、值或项(以 (键, 值) 为元素的元组表示)的迭代器。

键和值是按插入时的顺序进行迭代的。 这样就允许使用 zip() 来创建 (值, 键) 对: pairs = zip(d.values(), d.keys())。 另一个创建相同列表的方式是 pairs = [(v, k) for (k, v) in d.items()].

在添加或删除字典中的条目期间对视图进行迭代可能引发 RuntimeError 或者无法完全迭代所有条目。

在 3.7 版本发生变更: 字典顺序会确保为插入顺序。

  • x in dictview
  • 如果 x 是对应字典中存在的键、值或项(在最后一种情况下 x 应为一个 (键, 值) 元组) 则返回 True
  • reversed(dictview)
  • 返回一个逆序获取字典键、值或项的迭代器。 视图将按与插入时相反的顺序进行迭代。

在 3.8 版本发生变更: 字典视图现在是可逆的。

Added in version 3.10.

键视图与集合类似因为其条目是唯一的并且为 hashable。 条视图也有类似集合的操作因为 (键, 值) 对是唯一的并且键是可哈希的。 如果条目视图中的所有值也都是可哈希的,那么条目视图就可以与其他集合执行互操作。 (值视图不会被认为与集合类似因为条目通常不是唯一的)。 对于与集合类似的视图,可以使用为抽象基类 collections.abc.Set 定义的所有操作(例如,==, <^ 等)。 虽然使用了集合运算符,但与集合类似的视图接受任何可迭代对象作为其操作数,而不像集合那样只接受集合作为输入。

一个使用字典视图的示例:

  1. >>> dishes = {'eggs': 2, 'sausage': 1, 'bacon': 1, 'spam': 500}
  2. >>> keys = dishes.keys()
  3. >>> values = dishes.values()
  4.  
  5. >>> # 迭代
  6. >>> n = 0
  7. >>> for val in values:
  8. ... n += val
  9. ...
  10. >>> print(n)
  11. 504
  12.  
  13. >>> # 键和值将以相同顺序(插入顺序)被迭代
  14. >>> list(keys)
  15. ['eggs', 'sausage', 'bacon', 'spam']
  16. >>> list(values)
  17. [2, 1, 1, 500]
  18.  
  19. >>> # 视图对象是动态的并会反映字典的改变
  20. >>> del dishes['eggs']
  21. >>> del dishes['sausage']
  22. >>> list(keys)
  23. ['bacon', 'spam']
  24.  
  25. >>> # 集合运算
  26. >>> keys & {'eggs', 'bacon', 'salad'}
  27. {'bacon'}
  28. >>> keys ^ {'sausage', 'juice'} == {'juice', 'sausage', 'bacon', 'spam'}
  29. True
  30. >>> keys | ['juice', 'juice', 'juice'] == {'bacon', 'spam', 'juice'}
  31. True
  32.  
  33. >>> # 获取原始字典的只读代理
  34. >>> values.mapping
  35. mappingproxy({'bacon': 1, 'spam': 500})
  36. >>> values.mapping['spam']
  37. 500

上下文管理器类型

Python 的 with 语句支持通过上下文管理器所定义的运行时上下文这一概念。 此对象的实现使用了一对专门方法,允许用户自定义类来定义运行时上下文,在语句体被执行前进入该上下文,并在语句执行完毕时退出该上下文:

  • contextmanager.enter()
  • 进入运行时上下文并返回此对象或关联到该运行时上下文的其他对象。 此方法的返回值会绑定到使用此上下文管理器的 with 语句的 as 子句中的标识符。

一个返回其自身的上下文管理器的例子是 file object。 文件对象会从 enter() 返回其自身,以允许 open() 被用作 with 语句中的上下文表达式。

一个返回关联对象的上下文管理器的例子是 decimal.localcontext() 所返回的对象。 此种管理器会将活动的 decimal 上下文设为原始 decimal 上下文的一个副本并返回该副本。 这允许对 with 语句的语句体中的当前 decimal 上下文进行更改,而不会影响 with 语句以外的代码。

  • contextmanager.exit(exc_type, exc_val, exc_tb)
  • 退出运行时上下文并返回一个布尔值旗标来表明所发生的任何异常是否应当被屏蔽。 如果在执行 with 语句的语句体期间发生了异常,则参数会包含异常的类型、值以及回溯信息。 在其他情况下三个参数均为 None

自此方法返回一个真值将导致 with 语句屏蔽异常并继续执行紧随在 with 语句之后的语句。 否则异常将在此方法结束执行后继续传播。 在此方法执行期间发生的异常将会取代 with 语句的语句体中发生的任何异常。

传入的异常绝对不应当被显式地重新引发 —— 相反地,此方法应当返回一个假值以表明方法已成功完成并且不希望屏蔽被引发的异常。 这允许上下文管理代码方便地检测 __exit__() 方法是否确实已失败。

Python 定义了一些上下文管理器来支持简易的线程同步、文件或其他对象的快速关闭,以及更方便地操作活动的十进制算术上下文。 除了实现上下文管理协议以外,不同类型不会被特殊处理。 请参阅 contextlib 模块查看相关的示例。

Python 的 generatorcontextlib.contextmanager 装饰器提供了实现这些协议的便捷方式。 如果使用 contextlib.contextmanager 装饰器来装饰一个生成器函数,它将返回一个实现了必要的 __enter__()__exit__() 方法的上下文管理器,而不再是由未经装饰的生成器所产生的迭代器。

请注意,Python/C API 中 Python 对象的类型结构中并没有针对这些方法的专门槽位。 想要定义这些方法的扩展类型必须将它们作为普通的 Python 可访问方法来提供。 与设置运行时上下文的开销相比,单个类字典查找的开销可以忽略不计。

类型注解的类型 —- Generic AliasUnion

type annotations 的内置类型为 Generic AliasUnion

GenericAlias 类型

GenericAlias 对象通常是通过 抽取 一个类来创建的。 它们最常被用于 容器类,如 listdict。 举例来说,list[int] 这个 GenericAlias 对象是通过附带 int 参数抽取 list 类来创建的。 GenericAlias 对象的主要目的是用于 类型标注

备注

通常一个类只有在实现了特殊方法 __class_getitem__() 时才支持抽取操作。

GenericAlias 对象可作为 generic type 的代理,实现了 形参化泛型

对于一个容器类,提供给类的 抽取 操作的参数可以指明对象所包含的元素类型。 例如,set[bytes] 可在类型标注中用来表示一个 set 中的所有元素均为 bytes 类型。

对于一个定义了 __class_getitem__() 但不属于容器的类,提供给类的抽取操作的参数往往会指明在对象上定义的一个或多个方法的返回值类型。 例如,正则表达式 可以被用在 str 数据类型和 bytes 数据类型上:

  • 如果 x = re.search('foo', 'foo'),则 x 将为一个 re.Match 对象而 x.group(0)x[0] 的返回值将均为 str 类型。 我们可以在类型标注中使用 GenericAlias re.Match[str] 来代表这种对象。

  • 如果 y = re.search(b'bar', b'bar'),(注意 b 表示 bytes),则 y 也将为一个 re.Match 的实例,但 y.group(0)y[0] 的返回值将均为 bytes 类型。 在类型标注中,我们将使用 re.Match[bytes] 来代表这种形式的 re.Match 对象。

GenericAlias 对象是 types.GenericAlias 类的实例,该类也可被用来直接创建 GenericAlias 对象。

  • T[X, Y, …]
  • 创建一个代表由类型 X, Y 来参数化的类型 TGenericAlias,此类型会更依赖于所使用的 T。 例如,一个接受包含 float 元素的 list 的函数:
  1. def average(values: list[float]) -> float:
  2. return sum(values) / len(values)

另一个例子是关于 mapping 对象的,用到了 dict,泛型的两个类型参数分别代表了键类型和值类型。本例中的函数需要一个 dict,其键的类型为 str,值的类型为 int:。

  1. def send_post_request(url: str, body: dict[str, int]) -> None:
  2. ...

内置函数 isinstance()issubclass() 不接受第二个参数为 GenericAlias 类型:

  1. >>> isinstance([1, 2], list[str])
  2. Traceback (most recent call last):
  3. File "<stdin>", line 1, in <module>
  4. TypeError: isinstance() argument 2 cannot be a parameterized generic

Python 运行时不会强制执行 类型标注。 这种行为扩展到了泛型及其类型形参。 当由 GenericAlias 创建容器对象时,并不会检查容器中为元素指定的类型。 例如,以下代码虽然不被鼓励,但运行时并不会报错:

  1. >>> t = list[str]
  2. >>> t([1, 2, 3])
  3. [1, 2, 3]

不仅如此,在创建对象的过程中,应用了参数后的泛型还会抹除类型参数:

  1. >>> t = list[str]
  2. >>> type(t)
  3. <class 'types.GenericAlias'>
  4.  
  5. >>> l = t()
  6. >>> type(l)
  7. <class 'list'>

在泛型上调用 repr()str() 会显示应用参数之后的类型:

  1. >>> repr(list[int])
  2. 'list[int]'
  3.  
  4. >>> str(list[int])
  5. 'list[int]'

调用泛型容器的 __getitem__() 方法将引发异常以防出现 dict[str][str] 之类的错误:

  1. >>> dict[str][str]
  2. Traceback (most recent call last): ...
  3. TypeError: dict[str] is not a generic class

不过,当使用了 类型变量 时这种表达式是无效的。 索引必须有与 GenericAlias 对象的 __args__ 中的类型变量条目数量相当的元素。

  1. >>> from typing import TypeVar
  2. >>> Y = TypeVar('Y')
  3. >>> dict[str, Y][int]
  4. dict[str, int]

标准泛型类

下列标准库类支持形参化的泛型。 此列表并不是详尽无遗的。

GenericAlias 对象的特殊属性

应用参数后的泛型都实现了一些特殊的只读属性:

  • genericalias.origin
  • 本属性指向未应用参数之前的泛型类:
  1. >>> list[int].__origin__
  2. <class 'list'>
  • genericalias.args
  • 该属性是传给泛型类的原始 __class_getitem__() 的泛型所组成的 tuple (长度可能为 1):
  1. >>> dict[str, list[int]].__args__
  2. (<class 'str'>, list[int])
  • genericalias.parameters
  • 该属性是延迟计算出来的一个元组(可能为空),包含了 __args__ 中的类型变量。
  1. >>> from typing import TypeVar
  2.  
  3. >>> T = TypeVar('T')
  4. >>> list[T].__parameters__
  5. (~T,)

备注

带有参数 typing.ParamSpecGenericAlias 对象,在类型替换后其 __parameters__ 可能会不准确,因为 typing.ParamSpec 主要用于静态类型检查。

  • genericalias.unpacked
  • 一个布尔值,如果别名已使用 * 运算符进行解包则为真值 (参见 TypeVarTuple)。

Added in version 3.11.

参见

Added in version 3.9.

union 类型

联合对象包含了在多个 类型对象 上执行 | (按位或) 运算后的值。 这些类型主要用于 类型标注。与 typing.Union 相比,联合类型表达式可以实现更简洁的类型提示语法。

  • X | Y | …
  • 定义包含了 X、Y 等类型的 union 对象。 X | Y 表示 X 或 Y。相当于 typing.Union[X, Y] 。比如以下函数的参数应为类型 intfloat
  1. def square(number: int | float) -> int | float:
  2. return number ** 2

备注

不可在运行时使用 | 操作数来定义有一个或多个成员为前向引用的并集。 例如,int | "Foo",其中 "Foo" 是指向某个尚未定义的类的引用,在运行时将会失败。 对于包括前向引用的并集,请将整个表达式用字符串来表示,例如 "int | Foo"

  • union_object == other
  • union 对象可与其他 union 对象进行比较。详细结果如下:

    • 多次组合的结果会平推:
  1. (int | str) | float == int | str | float
  • 冗余的类型会被删除:
  1. int | str | int == int | str
  • 在相互比较时,会忽略顺序:
  1. int | str == str | int
  • typing.union 兼容:
  1. int | str == typing.Union[int, str]
  • Optional 类型可表示为与 None 的组合。
  1. str | None == typing.Optional[str]
  • isinstance(obj, union_object)
  1. >>> isinstance("", int | str)
  2. True

但是联合对象中的 参数化泛型 将无法被检测:

  1. >>> isinstance(1, int | list[int]) # 短路求值
  2. True
  3. >>> isinstance([1], int | list[int])
  4. Traceback (most recent call last): ...
  5. TypeError: isinstance() argument 2 cannot be a parameterized generic

union 对象构成的用户类型可以经由 types.UnionType 访问,并可用于 isinstance() 检查。 而不能由类型直接实例化为对象:

  1. >>> import types
  2. >>> isinstance(int | str, types.UnionType)
  3. True
  4. >>> types.UnionType()
  5. Traceback (most recent call last):
  6. File "<stdin>", line 1, in <module>
  7. TypeError: cannot create 'types.UnionType' instances

备注

为了支持 X | Y 语法,类型对象加入了 __or__() 方法。 如果一个元类实现了 __or__(),Union 可以重载它:

  1. >>> class M(type):
  2. ... def __or__(self, other):
  3. ... return "Hello"
  4. ...
  5. >>> class C(metaclass=M):
  6. ... pass
  7. ...
  8. >>> C | int
  9. 'Hello'
  10. >>> int | C
  11. int | C

参见

PEP 604 [https://peps.python.org/pep-0604/] —— 提出了 X | Y 语法和 union 类型。

Added in version 3.10.

其他内置类型

解释器支持一些其他种类的对象。 这些对象大都仅支持一两种操作。

模块

模块唯一的特殊操作是属性访问: m.name,这里 m 为一个模块而 name 访问定义在 m 的符号表中的一个名称。 模块属性可以被赋值。 (请注意 import 语句严格来说也是对模块对象的一种操作;import foo 不要求存在一个名为 foo 的模块对象,而是要求存在一个对于名为 foo 的模块的 (永久性) 定义。)

每个模块都有一个特殊属性 __dict__。 这是包含模块的符号表的字典。 修改此字典将实际改变模块的符号表,但是无法直接对 __dict__ 赋值 (你可以写 m.__dict__['a'] = 1,这会将 m.a 定义为 1,但是你不能写 m.__dict__ = {})。 不建议直接修改 __dict__

内置于解释器中的模块会写成这样: 。 如果是从一个文件加载,则会写成

类与类实例

关于这些类型请参阅 对象、值与类型类定义

函数

函数对象是通过函数定义创建的。 对函数对象的唯一操作是调用它: func(argument-list)

实际上存在两种不同的函数对象:内置函数和用户自定义函数。 两者支持同样的操作(调用函数),但实现方式不同,因此对象类型也不同。

更多信息请参阅 函数定义

方法

方法是使用属性表示法来调用的函数。 存在两种形式: 内置方法 (如列表的 append()) 和 类实例方法。 内置方法由支持它们的类型来描述。

如果你通过一个实例来访问方法(即定义在类命名空间内的函数),你会得到一个特殊对象: 绑定方法 (或称 实例方法) 对象。 当被调用时,它会将 self 参数添加到参数列表。 绑定方法具有两个特殊的只读属性: m.__self__ 操作该方法的对象,而 m.__func__ 是实现该方法的函数。 调用 m(arg-1, arg-2, …, arg-n) 完全等价于调用 m.__func__(m.__self__, arg-1, arg-2, …, arg-n)

函数对象 类似,绑定方法对象也支持获取任意属性。 但是,由于方法属性实际上保存于下层的函数对象中 (method.__func__),因此不允许设置绑定方法的方法属性。 尝试设置方法的属性将会导致引发 AttributeError。 想要设置方法属性,你必须在下层的函数对象中显式地设置它。

  1. >>> class C:
  2. ... def method(self):
  3. ... pass
  4. ...
  5. >>> c = C()
  6. >>> c.method.whoami = 'my name is method' # can't set on the method
  7. Traceback (most recent call last):
  8. File "<stdin>", line 1, in <module>
  9. AttributeError: 'method' object has no attribute 'whoami'
  10. >>> c.method.__func__.whoami = 'my name is method'
  11. >>> c.method.whoami
  12. 'my name is method'

请参阅 实例方法 了解更多信息。

代码对象

代码对象被具体实现用来表示“伪编译”的可执行 Python 代码例如一个函数体。 它们不同于函数对象,因为它们不包含对其全局执行环境的引用。 代码对象由内置的 compile() 函数返回,并可通过函数对象的 __code__ 属性来提取。 另请参阅 code 模块。

访问 __code__ 会引发一个 审计事件 object.__getattr__,并附带参数 obj"__code__"

可以通过将代码对象(而非源码字符串)传给 exec()eval() 内置函数来执行或求值。

更多信息请参阅 标准类型层级结构

类型对象

类型对象表示各种对象类型。 对象的类型可通过内置函数 type() 来获取。 类型没有特殊的操作。 标准库模块 types 定义了所有标准内置类型的名称。

类型以这样的写法来表示:

空对象

此对象会由不显式地返回值的函数所返回。 它不支持任何特殊的操作。 空对象只有一种值 None (这是个内置名称)。 type(None)() 会生成同一个单例。

该对象的写法为 None

省略符对象

此对象常被用于切片 (参见 切片)。 它不支持任何特殊的操作。 省略符对象只有一种值 Ellipsis (这是个内置名称)。 type(Ellipsis)() 会生成 Ellipsis 单例。

该对象的写法为 Ellipsis

未实现对象

此对象会被作为比较和二元运算被应用于它们所不支持的类型时的返回值。 请参阅 比较运算 了解更多信息。 未实现对象只有一种值 NotImplementedtype(NotImplemented)() 会生成这个单例。

其写法为 NotImplemented

内部对象

相关信息请参阅 标准类型层级结构。 其中描述了 栈帧对象, 回溯对象 以及切片对象等。

特殊属性

语言实现为部分对象类型添加了一些特殊的只读属性,它们具有各自的作用。 其中一些并不会被 dir() 内置函数所列出。

  • definition.name
  • 类、函数、方法、描述器或生成器实例的名称。
  • definition.qualname
  • 类、函数、方法、描述器或生成器实例的 qualified name

Added in version 3.3.

  • definition.module
  • 类或函数定义所在的模块的名称。
  • definition.doc
  • 类或函数的文档字符串,如果未定义则为 None
  • definition.type_params
  • 泛型类、函数和 类型别名类型形参。 对于非泛型类和函数,这将为空元组。

Added in version 3.12.

整数字符串转换长度限制

CPython 对于 intstr 之间的转换有一个全局限制以缓解拒绝服务攻击。 此限制 仅会 作用于十进制或其他以非二的乘方为基数的数字。 十六进制、八进制和二进制转换不受限制。 该限制可以被配置。

int 类型在 CPython 中是存储为二进制形式的任意长度的数字(通常称为“大数字”)。 不存在可在线性时间内将一个字符串转换为二进制整数或将一个二进制整数转换为字符串的算法,除非 基数为 2 的乘方。 对于基数为 10 来说已知最好的算法也有亚二次方复杂度。 转换一个大数值如 int('1' * 500_000) 在快速的 CPU 上也会花费一秒以上的时间。

限制转换大小是一项避免 CVE 2020-10735 [https://www.cve.org/CVERecord?id=CVE-2020-10735] 的务实解决方式。

此限制会在可能涉及非线性转换算法时作用于输入或输出字符串中的数字型字符数量。 下划线和正负号不计入限制数量。

当一个操作会超出限制时,将引发 ValueError:

  1. >>> import sys
  2. >>> sys.set_int_max_str_digits(4300) # 含义如名称所示,这是默认值。
  3. >>> _ = int('2' * 5432)
  4. Traceback (most recent call last):
  5. ...
  6. ValueError: Exceeds the limit (4300 digits) for integer string conversion: value has 5432 digits; use sys.set_int_max_str_digits() to increase the limit
  7. >>> i = int('2' * 4300)
  8. >>> len(str(i))
  9. 4300
  10. >>> i_squared = i*i
  11. >>> len(str(i_squared))
  12. Traceback (most recent call last):
  13. ...
  14. ValueError: Exceeds the limit (4300 digits) for integer string conversion; use sys.set_int_max_str_digits() to increase the limit
  15. >>> len(hex(i_squared))
  16. 7144
  17. >>> assert int(hex(i_squared), base=16) == i*i # 十六进制数没有限制。

默认限制为 4300 位即 sys.int_info.default_max_str_digits 的值。 最低限制可被配置为 640 位即 sys.int_info.str_digits_check_threshold

验证:

  1. >>> import sys
  2. >>> assert sys.int_info.default_max_str_digits == 4300, sys.int_info
  3. >>> assert sys.int_info.str_digits_check_threshold == 640, sys.int_info
  4. >>> msg = int('578966293710682886880994035146873798396722250538762761564'
  5. ... '9252925514383915483333812743580549779436104706260696366600'
  6. ... '571186405732').to_bytes(53, 'big')
  7. ...

Added in version 3.11.

受影响的 API

此限制仅会作用于 intstrbytes 之间存在速度变慢可能的转换:

  • int(string) 默认以 10 为基数。

  • int(string, base) 用于所有不为 2 的乘方的基数。

  • str(integer)

  • repr(integer)

  • 任何其他目标是以 10 为基数的字符串转换,例如 f"{integer}", "{}".format(integer)b"%d" % integer

此限制不会作用于使用线性算法的函数:

配置限制值

在 Python 启动之前你可以使用环境变量或解释器命令行旗标来配置限制值:

从代码中,你可以检查当前的限制并使用这些 sys API 来设置新值:

有关默认值和最小值的信息可在 sys.int_info 中找到:

Added in version 3.11.

小心

设置较低的限制值 可能 导致问题。 虽然不常见,但还是会有在其源代码中包含超出最小阈值的十进制整数常量的代码存在。 设置此限制的一个后果将是包含比此限制长的十进制整数字面值的 Python 源代码将在解析期间遇到错误,通常是在启动时或导入时甚至是在安装时 —— 只要对于某个代码还不存在已更新的 .pyc 就会发生。 一种在包含此类大数值常量的源代码中绕过该问题的办法是将它们转换为不受限制的 0x 十六进制形式。

如果你使用了较低的限制则请要彻底地测试你的应用程序。 确保你的测试通过环境变量或旗标尽早设置该限制来运行以便在启动期间甚至是在可能唤起 Python 来将 .py 源文件预编译为 .pyc 文件的任何安装步骤其间应用该限制。

推荐配置

默认的 sys.int_info.default_max_str_digits 被预期对于大多数应用程序来说都是合理的。 如果你的应用程序需要不同的限制值,请使用不预设 Python 版本的代码从你的主入口点进行设置,因为这些 API 是在 3.12 之前的版本所发布的安全补丁中添加的。

示例:

  1. >>> import sys
  2. >>> if hasattr(sys, "set_int_max_str_digits"):
  3. ... upper_bound = 68000
  4. ... lower_bound = 4004
  5. ... current_limit = sys.get_int_max_str_digits()
  6. ... if current_limit == 0 or current_limit > upper_bound:
  7. ... sys.set_int_max_str_digits(upper_bound)
  8. ... elif current_limit < lower_bound:
  9. ... sys.set_int_max_str_digits(lower_bound)

如果你需要完全禁用它,请将其设为 0

备注

[1] 有关这些特殊方法的额外信息可参看 Python 参考指南 (基本定制)。

[2] 作为结果,列表 [1, 2][1.0, 2.0] 是相等的,元组的情况也类似。

[3] 必须如此,因为解析器无法判断操作数的类型。

[4] (1,2,3,4) 区分大小写的字符是指所属一般类别属性为 "Lu" (Letter, uppercase), "Ll" (Letter, lowercase) 或 "Lt" (Letter, titlecase) 之一的字符。

[5] (1,2) 若只是要格式化一个元组,则应提供一个单例元组,其中只包含一个元素,就是需要格式化的那个元组。