3.数据模型

3.1.对象、值与类型

对象 是 Python 中对数据的抽象。 Python 程序中的所有数据都是由对象或对象间关系来表示的。 (从某种意义上说,按照冯·诺依曼的“存储程序计算机”模型,代码本身也是由对象来表示的。)

每个对象都有相应的标识号、类型和值。 一个对象被创建后它的标识号 就绝不会改变;你可以将其理解为该对象在内存中的地址。is 运算符比较两个对象的标识号是否相同;id() 函数返回一个代表其标识号的整数。

在 CPython 中,id(x) 就是存放x 的内存的地址。

对象的类型决定该对象所支持的操作 (例如 "对象是否有长度属性?") 并且定义了该类型的对象可能的取值。type() 函数能返回一个对象的类型 (类型本身也是对象)。与编号一样,一个对象的类型 也是不可改变的。[1]

有些对象的 可以改变。值可以改变的对象被称为可变对象;值不可以改变的对象就被称为不可变对象。(一个不可变容器对象如果包含对可变对象的引用,当后者的值改变时,前者的值也会改变;但是该容器仍属于不可变对象,因为它所包含的对象集是不会改变的。因此,不可变并不严格等同于值不能改变,实际含义要更微妙。) 一个对象的可变性是由其类型决定的;例如,数字、字符串和元组是不可变的,而字典和列表是可变的。

对象绝不会被显式地销毁;然而,当无法访问时它们可能会被作为垃圾回收。允许具体的实现推迟垃圾回收或完全省略此机制 --- 如何实现垃圾回收是实现的质量问题,只要可访问的对象不会被回收即可。

CPython 目前使用带有 (可选) 延迟检测循环链接垃圾的引用计数方案,会在对象不可访问时立即回收其中的大部分,但不保证回收包含循环引用的垃圾。请查看gc 模块的文档了解如何控制循环垃圾的收集相关信息。其他实现会有不同的行为方式,CPython 现有方式也可能改变。不要依赖不可访问对象的立即终结机制 (所以你应当总是显式地关闭文件)。

注意:使用实现的跟踪或调试功能可能令正常情况下会被回收的对象继续存活。还要注意通过try...except 语句捕捉异常也可能令对象保持存活。

有些对象包含对“外部”资源如打开的文件或窗口的引用。 当对象被作为垃圾回收时这些资源也应该会被释放,但由于垃圾回收并不确保发生,这些对象还提供了明确地释放外部资源的操作,通常为一个close() 方法。 强烈推荐在程序中显式关闭此类对象。try...finally 语句和with 语句提供了进行此种操作的更便捷方式。

有些对象包含对其他对象的引用;它们被称为容器。容器的例子有元组、列表和字典等。这些引用是容器对象值的组成部分。在多数情况下,当谈论一个容器的值时,我们是指所包含对象的值而不是其编号;但是,当我们谈论一个容器的可变性时,则仅指其直接包含的对象的编号。因此,如果一个不可变容器 (例如元组) 包含对一个可变对象的引用,则当该可变对象被改变时容器的值也会改变。

类型会影响对象行为的几乎所有方面。 甚至对象标识号的重要性也在某种程度上受到影响:对于不可变类型,计算新值的操作实际上可能返回一个指向具有相同类型和值的任何现存对象的引用,而对于可变对象来说这是不允许的。 例如在a=1;b=1 之后,ab 可能会也可能不会指向同一个值为一的对象。 这是因为int 是不可变对象,因此对1 的引用可以被重用。 此行为依赖于所使用的具体实现,因此不应该依赖它,而在使用对象标识测试时需要注意。 不过,在c=[];d=[] 之后,cd 保证会指向两个不同的、独特的、新创建的空列表。 (注意e=f=[] 会将同一个 对象同时赋值给ef。)

3.2.标准类型层级结构

以下是 Python 内置类型的列表。扩展模块 (具体实现会以 C, Java 或其他语言编写) 可以定义更多的类型。未来版本的 Python 可能会加入更多的类型 (例如有理数、高效存储的整型数组等等),不过新增类型往往都是通过标准库来提供的。

以下部分类型的描述中包含有 '特殊属性列表' 段落。这些属性提供对具体实现的访问而非通常使用。它们的定义在未来可能会改变。

3.2.1.None

此类型只有一种取值。是一个具有此值的单独对象。此对象通过内置名称None 访问。在许多情况下它被用来表示空值,例如未显式指明返回值的函数将返回 None。它的逻辑值为假。

3.2.2.NotImplemented

此类型只有一种取值。 是一个具有该值的单独对象。 此对象通过内置名称NotImplemented 访问。 数值方法和丰富比较方法如未实现指定运算符表示的运算则应返回该值。 (解释器会根据具体运算符继续尝试反向运算或其他回退操作。) 它不应被解读为布尔值。

详情参见实现算术运算

在 3.9 版本发生变更:EvaluatingNotImplemented in a boolean context is deprecated. Whileit currently evaluates as true, it will emit aDeprecationWarning.It will raise aTypeError in a future version of Python.

3.2.3.Ellipsis

此类型只有一种取值。是一个具有此值的单独对象。此对象通过字面值... 或内置名称Ellipsis 访问。它的逻辑值为真。

3.2.4.numbers.Number

此类对象由数字字面值创建,并会被作为算术运算符和算术内置函数的返回结果。数字对象是不可变的;一旦创建其值就不再改变。Python 中的数字当然非常类似数学中的数字,但也受限于计算机中的数字表示方法。

数字类的字符串表示形式,由__repr__()__str__() 算出,具有以下特征属性:

  • 它们是有效的数字字面值,当被传给它们的类构造器时,将会产生具有原数字值的对象。

  • 表示形式会在可能的情况下采用 10 进制。

  • 开头的零,除小数点前可能存在的单个零之外,将不会被显示。

  • 末尾的零,除小数点后可能存在的单个零之外,将不会被显示。

  • 正负号仅在当数字为负值时会被显示。

Python 区分整型数、浮点型数和复数:

3.2.4.1.numbers.Integral

此类对象表示数学中整数集合的成员 (包括正数和负数)。

备注

整型数表示规则的目的是在涉及负整型数的变换和掩码运算时提供最为合理的解释。

整型数可细分为两种类型:

整型 (int)

此类对象表示任意大小的数字,仅受限于可用的内存 (包括虚拟内存)。在变换和掩码运算中会以二进制表示,负数会以 2 的补码表示,看起来像是符号位向左延伸补满空位。

布尔型 (bool)

此类对象表示逻辑值 False 和 True。代表FalseTrue 值的两个对象是唯二的布尔对象。布尔类型是整型的子类型,两个布尔值在各种场合的行为分别类似于数值 0 和 1,例外情况只有在转换为字符串时分别返回字符串"False""True"

3.2.4.2.numbers.Real (float)

此类对象表示机器级的双精度浮点数。其所接受的取值范围和溢出处理将受制于底层的机器架构 (以及 C 或 Java 实现)。Python 不支持单精度浮点数;支持后者通常的理由是节省处理器和内存消耗,但这点节省相对于在 Python 中使用对象的开销来说太过微不足道,因此没有理由包含两种浮点数而令该语言变得复杂。

3.2.4.3.numbers.Complex (complex)

此类对象以一对机器级的双精度浮点数来表示复数值。有关浮点数的附带规则对其同样有效。一个复数值z 的实部和虚部可通过只读属性z.realz.imag 来获取。

3.2.5.序列

这些代表以非负数为索引的有限有序集合。 内置函数len() 将返回序列的项数。 当序列 的长度为n 时,索引集合将包含数字 0, 1, ...,n-1。a[i] 是选择序列a 中的第i 项。 某些序列,包括内置的序列,可通过加上序列长度来解读负下标值。 例如,a[-2] 等价于a[n-2],即长度为n 的 a 序列的倒数第二项。

序列还支持切片:a[i:j] 是选择索引为k 使得i<=k<j 的所有条目。 当用作表达式时,切片就是一个相同类型的新序列。 以上有关负索引的注释也适用于切片位置的负值。

有些序列还支持带有第三个 "step" 形参的 "扩展切片":a[i:j:k] 选择a 中索引号为x 的所有条目,x=i+n*k,n>=0i<=x<j

序列可根据其可变性来加以区分:

3.2.5.1.不可变序列

不可变序列类型的对象一旦创建就不能再改变。(如果对象包含对其他对象的引用,其中的可变对象就是可以改变的;但是,一个不可变对象所直接引用的对象集是不能改变的。)

以下类型属于不可变对象:

字符串

字符串是由代表 Unicode 码位的值组成的序列。 取值范围在U+0000-U+10FFFF 之内的所有码位都可在字符串中使用。 Python 没有char 类型;而是将字符串中的每个码位表示为一个长度为1 的字符串对象。 内置函数ord() 可将一个码位由字符串形式转换为取值范围在0-10FFFF 之内的整数;chr() 可将一个取值范围在0-10FFFF 之内的整数转换为长度为1 的对应字符串对象。str.encode() 可以使用给定的文本编码格式将str 转换为bytes,而bytes.decode() 则可以被用来实现相反的解码操作。

元组

一个元组中的条目可以是任意 Python 对象。包含两个或以上条目的元组由逗号分隔的表达式构成。只有一个条目的元组 ('单项元组') 可通过在表达式后加一个逗号来构成 (一个表达式本身不能创建为元组,因为圆括号要用来设置表达式分组)。一个空元组可通过一对内容为空的圆括号创建。

字节串

字节串对象是不是变的数组。 其中的条目是 8 比特位的字节,以取值范围 0 <= x < 256 内的整数表示。 字节串字面值 (如b'abc') 和内置的bytes() 构造器可被用来创建字节串对象。 并且,字节串对象还可通过decode() 方法被解码为字符串。

3.2.5.2.可变序列

可变序列在被创建后仍可被改变。下标和切片标注可被用作赋值和del (删除) 语句的目标。

备注

collectionsarray 模块提供了可变序列类型的更多例子。

目前有两种内生可变序列类型:

列表

列表中的条目可以是任意 Python 对象。列表由用方括号括起并由逗号分隔的多个表达式构成。(注意创建长度为 0 或 1 的列表无需使用特殊规则。)

字节数组

字节数组对象属于可变数组。可以通过内置的bytearray() 构造器来创建。除了是可变的 (因而也是不可哈希的),在其他方面字节数组提供的接口和功能都与不可变的bytes 对象一致。

3.2.6.集合类型

此类对象表示由不重复且不可变对象组成的无序且有限的集合。因此它们不能通过下标来索引。但是它们可被迭代,也可用内置函数len() 返回集合中的条目数。集合常见的用处是快速成员检测,去除序列中的重复项,以及进行交、并、差和对称差等数学运算。

对于集合元素所采用的不可变规则与字典的键相同。注意数字类型遵循正常的数字比较规则: 如果两个数字相等 (例如11.0),则同一集合中只能包含其中一个。

目前有两种内生集合类型:

集合

此类对象表示可变集合。它们可通过内置的set() 构造器创建,并且创建之后可以通过方法进行修改,例如add()

冻结集合

此类对象表示不可变集合。它们可通过内置的frozenset() 构造器创建。由于 frozenset 对象不可变且hashable,它可以被用作另一个集合的元素或是字典的键。

3.2.7.映射

此类对象表示由任意索引集合所索引的对象的集合。通过下标a[k] 可在映射a 中选择索引为k 的条目;这可以在表达式中使用,也可作为赋值或del 语句的目标。内置函数len() 可返回一个映射中的条目数。

目前只有一种内生映射类型:

3.2.7.1.字典

此类对象表示由几乎任意值作为索引的有限个对象的集合。不可作为键的值类型只有包含列表或字典或其他可变类型,通过值而非对象编号进行比较的值,其原因在于高效的字典实现需要使用键的哈希值以保持一致性。用作键的数字类型遵循正常的数字比较规则: 如果两个数字相等 (例如11.0) 则它们均可来用来索引同一个字典条目。

字典会保留插入顺序,这意味着键将以它们被添加的顺序在字典中依次产生。 替换某个现有的键不会改变其顺序,但是移除某个键再重新插入则会将其添加到末尾而不会保留其原有位置。

字典是可变对象;它们可通过{} 标注方式来创建(参见字典显示 一节)。

扩展模块dbm.ndbmdbm.gnu 提供了额外的映射类型示例,collections 模块也是如此。

在 3.7 版本发生变更:在 Python 3.6 版之前字典不会保留插入顺序。 在 CPython 3.6 中插入顺序会被保留,但这在当时被当作是一个实现细节而非确定的语言特性。

3.2.8.可调用类型

此类型可以被应用于函数调用操作 (参见调用 小节):

3.2.8.1.用户定义函数

用户定义函数对象可通过函数定义来创建 (参见函数定义 小节)。它被调用时应附带一个参数列表,其中包含的条目应与函数所定义的形参列表一致。

3.2.8.1.1.特殊的只读属性

属性

含意

function.__globals__

对存放该函数中全局变量字典 的引用 —— 函数定义所在模块的全局命名空间。

function.__closure__

None 或单元的tuple,其中包含了名称在函数的代码对象co_freevars 中对指定名称的绑定。

单元对象具有cell_contents 属性。这可被用来获取以及设置单元的值。

3.2.8.1.2.特殊的可写属性

这些属性大多会检查赋值的类型:

属性

含意

function.__doc__

函数的文档字符串,或者如果不可用则为None

function.__name__

函数的名称。 另请参阅:__name__属性

function.__qualname__

函数的qualified name。 另请参阅:__qualname__属性

Added in version 3.3.

function.__module__

该函数所属模块的名称,没有则为None

function.__defaults__

由具有默认值的形参的默认parameter 值组成的tuple,或者如果无任何形参具有默认值则为None

function.__code__

代表已编译的函数体的代码对象

function.__dict__

命名空间支持任意函数属性。 另请参阅:__dict__属性

function.__annotations__

Adictionary containing annotations ofparameters.The keys of the dictionary are the parameter names,and'return' for the return annotation, if provided.See also:注解最佳实践.

function.__kwdefaults__

包含仅限关键字形参 默认值的字典

function.__type_params__

包含泛型函数类型形参tuple

Added in version 3.12.

函数对象也支持获取和设置任意属性,举例来说,这可被用于将元数据关联到函数。 通常使用带点号的属性标注来获取和设置这样的属性。

CPython 目前的实现仅支持用户自定义函数上的函数属性。 未来可能会支持内置函数 上的函数属性。

有关函数定义的额外信息可以从其代码对象 中提取(可通过__code__ 属性来访问)。

3.2.8.2.实例方法

实例方法用于结合类、类实例和任何可调用对象 (通常为用户定义函数)。

特殊的只读属性:

method.__self__

指向方法所绑定 的类实例对象。

method.__func__

指向原本的函数对象

method.__doc__

方法的文档 (等同于method.__func__.__doc__)。 如果原始函数具有文档字符串则为一个字符串,否则为None

method.__name__

方法名称(与method.__func__.__name__ 相同)

method.__module__

方法定义所在模块的名称,如不可用则为None

方法还支持读取(但不能设置)下层函数对象 的任意函数属性。

用户自定义方法对象可在获取一个类的属性(可能是通过该类的实例)时被创建,如果该属性是一个用户自定义函数对象classmethod 对象的话。

当通过从类的实例获取一个用户自定义函数对象 的方式创建一个实例方法对象时,该方法对象的__self__ 属性即为该实例,而该方法对象将被称作已绑定。 该新建方法的__func__ 属性将是原来的函数对象。

当通过从类或实例获取一个classmethod 对象的方式创建一个实例方法对象时,该对象的__self__ 属性即为该类本身,而其__func__ 属性将是类方法对应的下层函数对象。

当一个实例方法被调用时,会调用对应的下层函数 (__func__),并将类实例 (__self__) 插入参数列表的开头。 例如,当C 是一个包含f() 函数定义的类,而xC 的一个实例,则调用x.f(1) 就等价于调用C.f(x,1)

当一个实例方法对象是派生自一个classmethod 对象时,保存在__self__ 中的“类实例”实际上会是该类本身,因此无论是调用x.f(1) 还是C.f(1) 都等同于调用f(C,1),其中f 为对应的下层函数。

需要重点关注的是作为类实例的属性的用户自定义函数不会被转换为绑定方法;这只会 在函数是类的属性时才会发生。

3.2.8.3.生成器函数

一个使用yield 语句(见yield 语句 章节)的函数或方法被称为生成器函数。 这样的函数在被调用时,总是返回一个可以执行该函数体的iterator 对象:调用该迭代器的iterator.__next__() 方法将导致这个函数一直运行到它使用yield 语句提供一个值。 当这个函数执行return 语句或到达函数体末尾时,将引发StopIteration 异常并且该迭代器将到达所返回的值集合的末尾。

3.2.8.4.协程函数

使用asyncdef 来定义的函数或方法就被称为协程函数。这样的函数在被调用时会返回一个coroutine 对象。它可能包含await 表达式以及asyncwithasyncfor 语句。详情可参见协程对象 一节。

3.2.8.5.异步生成器函数

使用asyncdef 来定义并使用了yield 语句的函数或方法被称为异步生成器函数。 这样的函数在被调用时,将返回一个asynchronous iterator 对象,该对象可在asyncfor 语句中被用来执行函数体。

调用异步迭代器的aiterator.__anext__ 方法将返回一个awaitable,此对象会在被等待时执行直到使用yield 产生一个值。 当函数执行到空的return 语句或函数末尾时,将会引发StopAsyncIteration 异常并且异步迭代器也将到达要产生的值集合的末尾。

3.2.8.6.内置函数

内置函数是针对特定 C 函数的包装器。 内置函数的例子包括len()math.sin() 等 (math 是一个标准内置模块)。 参数的数量和类型是由 C 函数确定的。 特殊的只读属性:

  • __doc__ 是函数的文档字符串,或者如果不可用则为None。 参见function.__doc__

  • __name__ 是函数的名称。 参见function.__name__

  • __self__ 被设为None (但请参见下一项)。

  • __module__ 是函数定义所在模块的名称,或者如果不可用则为None。 参见function.__module__

3.2.8.7.内置方法

此类型实际上是内置函数的另一种形式,只不过还包含了一个转入 C 函数的对象作为隐式的额外参数。 内置方法的一个例子是alist.append(),其中alist 是一个列表对象。 在此示例中,特殊的只读属性__self__ 会被设为alist 所标记的对象。 (该属性的语义与其他实例方法 的相同。)

3.2.8.8.

类是可调用对象。 这些对象通常是用作创建自身实例的“工厂”,但类也可以有重载__new__() 的变体类型。 调用的参数会传递给__new__(),并且在通常情况下,也会传递给__init__() 来初始化新的实例。

3.2.8.9.类实例

任意类的实例可以通过在其所属类中定义__call__() 方法变成可调用对象。

3.2.9.模块

模块是 Python 代码的基本组织单元,由导入系统 创建,它或是通过import 语句,或是通过调用importlib.import_module() 和内置的__import__() 等函数来唤起。 模块对象具有通过字典 对象实现的命名空间(就是被定义在模块中的函数的__globals__ 属性所引用的字典)。 属性引用将被转换为在该字典中的查找操作,例如m.x 就等价于m.__dict__["x"]。 模块对象不包含用于初始化模块的代码对象(因为初始化完成后已不再需要它)。

属性赋值会更新模块的命名空间字典,例如m.x=1 等同于m.__dict__["x"]=1

3.2.9.1.模块对象上与导入相关的属性

模块对象具有下列与导入系统 相关的属性。 当使用关联到导入系统的机制创建模块时,这些属性将在loader 执行和加载模块之前基于模块的规格 来填充。

要动态创建模块而非使用导入系统创建,建议使用importlib.util.module_from_spec(),它会将各种由导入控制的属性设置为适当的值。也可以使用types.ModuleType 构造器直接创建模块,但这种方法更容易出错,因为在使用这种方法时,必须在创建模块对象后手动设置其大部分属性。

小心

__name__ 以外的用例,强烈 建议使用__spec__ 及其属性,而非此处列出的其他单独属性。请注意更新__spec__ 上的属性时不会连带更新模块本身的同名属性。

>>>importtyping>>>typing.__name__,typing.__spec__.name('typing', 'typing')>>>typing.__spec__.name='spelling'>>>typing.__name__,typing.__spec__.name('typing', 'spelling')>>>typing.__name__='keyboard_smashing'>>>typing.__name__,typing.__spec__.name('keyboard_smashing', 'spelling')
module.__name__

用于在导入系统中唯一地标识模块的名称。 对于直接执行的模块,这将被设为"__main__"

该属性必须被设为模块的完整限定名称。 它应当与module.__spec__.name 的值相匹配。

module.__spec__

模块与导入系统相关联的状态的记录。

设置为导入模块时使用的模块规格。 请参阅模块规格说明 了解详情。

Added in version 3.4.

module.__package__

模块所属的package

如果一个模块是顶层模块(不是任何包的一部分),该属性应被设置为空字符串''。否则,它应被设置为模块所属包的名字(如果模块本身是一个包,它可以是module.__name__ 的值)。详见PEP 366

在为主模块计算显式相对导入时,这个属性而非__name__ 被使用。在使用types.ModuleType 构造器动态创建模块时会被默认设为None。要确保这个属性是str,请使用importlib.util.module_from_spec()

强烈 建议使用module.__spec__.parent 而非module.__package____package__ 现在仅作__spec__.parent 未被设置时的回退路径,且这条回退路径已被弃用。

在 3.4 版本发生变更:对于使用types.ModuleType 构造器动态创建的模块,该属性现在默认为None。先前该属性是可选的。

在 3.6 版本发生变更:__package__ 的值应与__spec__.parent 相同。__package__ 现在仅作导入解析期间__spec__.parent 未被定义时的回退值。

在 3.10 版本发生变更:如果导入解析回退到__package__ 而非__spec__.parent,会引发ImportWarning

在 3.12 版本发生变更:在导入解析期间回退到__package__ 时会引发DeprecationWarning 而非ImportWarning

Deprecated since version 3.13, will be removed in version 3.15:__package__ 将不再被设置或者被导入系统或标准库纳入考量。

module.__loader__

导入系统用来加载模块的loader 对象。

该属性主要适用于内省,但也可用于额外的加载器专属功能,例如获取与加载器相关联的数据。

对于使用types.ModuleType 构造器动态创建的模块__loader__ 默认为None;请改用importlib.util.module_from_spec() 来确保将该属性设为loader 对象。

强烈 建议你使用module.__spec__.loader 来代替module.__loader__

在 3.4 版本发生变更:对于使用types.ModuleType 构造器动态创建的模块,该属性现在默认为None。先前该属性是可选的。

Deprecated since version 3.12, will be removed in version 3.16:当设置__spec__.loader 失败时在模块上设置__loader__ 的做法已被弃用。 在 Python 3.16 中,__loader__ 将不会再被设置或被导入系统或标准库纳入考虑。

module.__path__

一个(可能为空的)枚举用于查找包的子模块的位置的由字符串组成的sequence。 非包模块应当没有__path__ 属性。 详情参见模块的 __path__ 属性

强烈 建议你使用module.__spec__.submodule_search_locations 来代替module.__path__

module.__file__
module.__cached__

__file____cached__ 都是可设也可不设的可选属性。 两个属性在可用时都应当为str

__file__ 指明要载入的模块所在文件的路径名(如果是从文件载入),或者对于从共享库动态载入的扩展模块来说则是共享库文件的路径名。 它对于特定类型的模块来说可能是缺失的,例如静态链接到解释器中的 C 模块,并且导入系统 也可能会在它没有语法意义时选择不设置它(例如,当一个模块是从数据库导入时)。

如果设置了__file____cached__ 属性也可能会被设置,它是指向任何代码的已编译版本的路径(例如,一个字节码文件)。 设置此属性并不需要存在相应的路径;该路径可以简单地指向已编译文件将要 存在的位置 (参见PEP 3147)。

请注意__cached__ 即使在未设置__file__ 时也可能会被设置。 不过,那样的场景是非典型的。 最终,loader 会是finder (from which__file____cached__ 也是自它派生的) 所提供的模块规格的使用方。 因此如果一个加载器可以从缓存加载模块但是不能从文件加载,那样的非典型场景就是适当的。

强烈 建议你使用module.__spec__.cached 来代替module.__cached__

Deprecated since version 3.13, will be removed in version 3.15:当设置__spec__.cached 失败时在模块上设置__cached__ 的做法已被弃用。 在 Python 3.15 中,__cached__ 将不会再被设置或被导入系统或标准库纳入考虑。

3.2.9.2.模块对象上的其他可写属性

除了上面列出的导入相关属性,模块对象还具有下列可写属性:

module.__doc__

模块的文档字符串,或者如果不可用则为None。 另请参阅:__doc__属性

module.__annotations__

A dictionary containingvariable annotations collected during modulebody execution. For best practices on working with__annotations__,please see注解最佳实践.

3.2.9.3.模块字典

模块对象还具有以下特殊的只读属性:Module objects also have the following special read-only attribute:

module.__dict__

以字典对象表示的模块命名空间。 在此处列出的属性中它很特别,__dict__ 不能从模块内部作为全局变量来访问;它只能作为模块对象上的属性来访问。

由于 CPython 清理模块字典的设定,当模块离开作用域时模块字典将会被清理,即使该字典还有活动的引用。想避免此问题,可复制该字典或保持模块状态以直接使用其字典。

3.2.10.自定义类

自定义类这种类型一般是通过类定义来创建 (参见类定义 一节)。 每个类都有一个通过字典对象实现的命名空间。 类属性引用会被转化为在此字典中查找,例如,C.x 会被转化为C.__dict__["x"] (不过也存在一些钩子对象允许其他定位属性的方式)。 当未在其中找到某个属性名称时,会继续在基类中查找。这种基类搜索使用 C3 方法解析顺序,即使存在 '钻石形' 继承结构既有多条继承路径连到一个共同祖先也能保持正确的行为。 有关 Python 使用的 C3 MRO 的详情可在Python 2.3 方法解析顺序 查看。

当一个类属性引用 (假设类名为C) 会产生一个类方法对象时,它将转化为一个__self__ 属性为C 的实例方法对象。 当它会产生一个staticmethod 对象时,它将转换为该静态方法对象所包装的对象。 有关有类的__dict__ 实际包含内容以外获取属性的其他方式请参阅实现描述器 一节。

类属性赋值会更新类的字典,但不会更新基类的字典。

类对象可被调用 (见上文) 以产生一个类实例 (见下文)。

3.2.10.1.特殊属性

属性

含意

type.__name__

类的名称。 另请参阅:__name__属性

type.__qualname__

类的qualified name。 另请参阅:__qualname__属性

type.__module__

类定义所在模块的名称。

type.__dict__

一个提供类的命名空间的只读视图的映射代理。 另请参阅:__dict__属性

type.__bases__

一个包含类的基类的tuple,对于定义为classX(A,B,C) 的类,X.__bases__ 将等于(A,B,C)

type.__doc__

类的文档字符串,如果未定义则为None。 不会被子类继承。 if undefined. Not inherited by subclasses.

type.__annotations__

A dictionary containingvariable annotationscollected during class body execution. For best practices on workingwith__annotations__, please see注解最佳实践.

小心

Accessing the__annotations__ attribute of a classobject directly may yield incorrect results in the presence ofmetaclasses. In addition, the attribute may not exist forsome classes. Useinspect.get_annotations() toretrieve class annotations safely.

type.__type_params__

一个包含泛型类类型形参tuple

Added in version 3.12.

type.__static_attributes__

一个包含由通过self.X 赋值为该类语句体中任何函数的类属性名称组成的tuple

Added in version 3.13.

type.__firstlineno__

类定义的第一行的行号。 设置__module__ 属性将从类型的字典中移除__firstlineno__ 条目。

Added in version 3.13.

type.__mro__

由在方法解析期间当查找基类时将被纳入考虑的类组成的tuple

3.2.10.2.特殊方法

除了上面介绍的特殊属性,所有的 Python 类还具有以下两个方法:

type.mro()

此方法可由一个元类来重写以便为其实例定制方法解析顺序。 它会在类实例化时被调用,其结果将存储在__mro__ 中。

type.__subclasses__()

每个类都会保存一个由指向其直接子类的弱引用组成的列表。 此方法将返回一个由所有仍然存在的这种引用组成的列表。 列表项将按定义顺序排列。 例如:

>>>classA:pass>>>classB(A):pass>>>A.__subclasses__()[<class 'B'>]

3.2.11.类实例

类实例可通过调用类对象来创建(见上文)。 每个类实例都有通过一个字典对象实现的独立命名空间,属性引用会首先在此字典中进行查找。 当未在其中发现某个属性,而实例对应的类中有该属性时,会继续在类属性中查找。 如果找到的类属性是一个用户自定义函数对象,它会被转化为实例方法对象,其__self__ 属性即该实例。 静态方法和类方法对象也会被转化;参见上文的“类”小节。 要了解其他通过类实例来获取相应类属性的方式请参阅实现描述器 小节,这样得到的属性可能与实际存放在类的__dict__ 中的对象不同。 如果未找到类属性,而对象所属的类具有__getattr__() 方法,则会调用该方法来满足查找要求。

属性赋值和删除会更新实例的字典,但绝不会更新类的字典。 如果类具有__setattr__()__delattr__() 方法,则将调用该方法而不再直接更新实例的字典。

如果类实例具有某些特殊名称的方法,就可以伪装为数字、序列或映射。参见特殊方法名称 一节。

3.2.11.1.特殊属性

object.__class__

类实例所属的类。

object.__dict__

一个用于存储对象的(可写)属性的字典或其他映射对象。 并非所有实例都具有__dict__ 属性;请参阅__slots__ 章节了解详情。

3.2.12.I/O 对象 (或称文件对象)

file object 表示一个打开的文件。有多种快捷方式可用来创建文件对象:open() 内置函数,以及os.popen(),os.fdopen() 和 socket 对象的makefile() 方法 (还可能使用某些扩展模块所提供的其他函数或方法)。

sys.stdin,sys.stdoutsys.stderr 会初始化为对应于解释器标准输入、输出和错误流的文件对象;它们都会以文本模式打开,因此都遵循io.TextIOBase 抽象类所定义的接口。

3.2.13.内部类型

某些由解释器内部使用的类型也被暴露给用户。它们的定义可能随未来解释器版本的更新而变化,为内容完整起见在此处一并介绍。

3.2.13.1.代码对象

代码对象表示编译为字节的 可执行 Python 代码,或称bytecode。代码对象和函数对象的区别在于函数对象包含对函数全局对象 (函数所属的模块) 的显式引用,而代码对象不包含上下文;而且默认参数值会存放于函数对象而不是代码对象内 (因为它们表示在运行时算出的值)。与函数对象不同,代码对象不可变,也不包含对可变对象的引用 (不论是直接还是间接)。

3.2.13.1.1.特殊的只读属性
codeobject.co_name

函数名

codeobject.co_qualname

完整限定函数名

Added in version 3.11.

codeobject.co_argcount

函数的位置形参 的总数(包括仅限位置形参和具有默认值的形参)

codeobject.co_posonlyargcount

函数的仅限位置形参 的总数(包括具有默认值的参数)

codeobject.co_kwonlyargcount

函数的仅限关键字形参 的数量(包括具有默认值的参数)

codeobject.co_nlocals

函数使用的局部变量 的数量(包括形参)

codeobject.co_varnames

一个tuple,其中包含函数中局部变量的名称(从形参名称开始)

codeobject.co_cellvars

包含被函数内至少一个nested scope 所引用的局部变量 的名称的tuple

codeobject.co_freevars

一个tuple,其中包含某个nested scope 引用在外部作用域中引用的 containing the names of自由(闭包)变量 的名称。 另请参阅function.__closure__

注意:对全局和内置名称的引用不会 被包括在内。

codeobject.co_code

一个表示函数中的bytecode 指令序列的字符串

codeobject.co_consts

一个包含函数中的bytecode 所使用的字面值的tuple

codeobject.co_names

一个包含函数中的bytecode 所使用的名称的tuple

codeobject.co_filename

被编译代码所在文件的名称

codeobject.co_firstlineno

函数第一行所对应的行号

codeobject.co_lnotab

一个编码了从bytecode 偏移量到行号的映射的字符串。 要获取更多细节,请查看解释器的源代码。

自 3.12 版本弃用:代码对象的这个属性已被弃用,并可能在 Python 3.15 中移除。

codeobject.co_stacksize

需要的代码对象栈大小

codeobject.co_flags

用于对一系列解释器旗标进行编码的整数

以下是针对co_flags 定义的旗标位:如果函数使用*arguments 语法来接受任意数量的位置参数则设置0x04 位;如果函数使用**keywords 语法来接受任意数量的关键字参数则设置0x08 位;如果函数是一个生成器则设置0x20 位。 请参阅代码对象位标志 可能出现的每个旗标的语义详情。

未来特性声明 (例如from__future__importdivision) 也使用co_flags 中的比特位来指明代码对象在编译时是否启用了某个特性。 参见compiler_flag

co_flags 中的其他位被保留供内部使用。

If a code object represents a function, the first item inco_consts isthe documentation string of the function, orNone if undefined.

3.2.13.1.2.代码对象的方法
codeobject.co_positions()

返回一个包含代码对象中每条bytecode 指令的源代码位置的可迭代对象。

此迭代器返回包含(start_line,end_line,start_column,end_column)tuple。 其中第i 个元组冲锋衣官方编译为第i 个代码单元的源代码的位置。 列信息是给定源代码行从 0 开始索引的 utf-8 字节偏移量。

此位置信息可能会丢失。 可能发生这种情况下非详尽列表如下:

  • 附带-Xno_debug_ranges 运行解释器。

  • 在使用-Xno_debug_ranges 时加载一个已编译的 pyc 文件。

  • 与人工指令相对应的位置元组。

  • 由于具体实现专属的限制而无法表示的行号和列号。

当发生此情况时,元组的部分或全部元素可以为None

Added in version 3.11.

备注

此特性需要在代码对象中存储列位置,这可能会导致编译的 which may result in a small increase of disk usage of compiled Python 文件占用的磁盘空间或解释器占用的内存略有增加。 要避免存储额外信息和/或取消打印额外的回溯信息,可以使用-Xno_debug_ranges 命令行旗标或PYTHONNODEBUGRANGES 环境变量。

codeobject.co_lines()

返回一个产生有关bytecode 的连续范围的信息的迭代器。 其产生的每一项都是一个(start,end,lineno)tuple:

  • start (一个int) 代表相对于bytecode 范围开始位置的偏移量 (不包括该位置)。

  • end (int 值) 代表相对于bytecode 范围末尾位置的偏移量(不包括该位置)。

  • lineno 是一个代表bytecode 范围内的行号的int,或者如果给定范围内的字节码没有行号则为None

产生的条目将具有下列特征属性:

  • 产出的第一个范围将以 0 作为start

  • (start,end) 范围将是非递减和连续的。 也就是说,对于任何一对tuple,第二个的start 将等于第一个的end

  • 任何范围都不会是反向的:对于所有三元组均有end>=start

  • 产生的最后一个tupleend 将等于bytecode 的大小。

零宽度范围,即start==end 也是允许的。 零宽度范围的使用场景是源代码中存在,但被bytecode 编译器所去除的那些行。

Added in version 3.10.

参见

PEP 626 - 在调试和其他工具中使用精确的行号。

引入co_lines() 方法的 PEP。

codeobject.replace(**kwargs)

返回代码对象的一个副本,使用指定的新字段值。

代码对象也被泛型函数copy.replace() 所支持。

Added in version 3.8.

3.2.13.2.帧对象

帧对象表示执行帧。 它们可能出现在回溯对象 中,还会被传递给已注册的跟踪函数。

3.2.13.2.1.特殊的只读属性
frame.f_back

指向前一个栈帧(对于调用方而言),或者如果这是最底部的栈帧则为None

frame.f_code

该帧中正在执行的代码对象。 访问该属性将引发一个审计事件object.__getattr__,附带参数obj"f_code"

frame.f_locals

被该帧用来查找局部变量 的映射。 如果该帧指向一个optimized scope,这可能返回一个直通写入代理对象。

在 3.13 版本发生变更:返回一个已优化作用域的代理。

frame.f_globals

被帧用于查找全局变量 的字典

frame.f_builtins

被帧用于查找内置(内建)名称 的字典

frame.f_lasti

帧对象的“准确指令”(这是代码对象bytecode 字符串的索引)

3.2.13.2.2.特殊的可写属性
frame.f_trace

如果不为None,则是在代码执行期间调用各类事件的函数 (由调试器使用)。 通常每个新的源代码行会触发一个事件 (参见f_trace_lines)。

frame.f_trace_lines

将该属性设为False 以禁用为每个源代码行触发跟踪事件。

frame.f_trace_opcodes

将该属性设为True 以允许请求每个操作码事件。 请注意如果跟踪函数引发的异常逃逸到被跟踪的函数中这可能会导致未定义的解释器行为。

frame.f_lineno

该帧的当前行号 -- 在这里写入从一个跟踪函数内部跳转到的给定行(仅用于最底层的帧)。 调试器可以通过写入该属性实现一个 Jump 命令(即设置下一条语句)。

3.2.13.2.3.帧对象方法

帧对象支持一个方法:

frame.clear()

此方法将清除该帧持有的全部对局部变量 的引用。 并且,如果该帧归属于一个generator,此生成器将被终结。 这有助于打破涉及帧对象的循环引用(例如当捕获一个异常 并保存其回溯 供以后使用)。

如果该帧当前正在执行或已挂起则会引发RuntimeError

Added in version 3.4.

在 3.13 版本发生变更:尝试清除已挂起的帧将引发RuntimeError (执行帧的情况将总是如此)。

3.2.13.3.回溯对象

回溯对象代表一个异常 的栈跟踪信息。 当异常发生时会隐式地创建一个回溯对象,也可以通过调用types.TracebackType 显式地创建。

在 3.7 版本发生变更:现在回溯对象可以通过 Python 代码显式地实例化。

对于隐式地创建的回溯对象,当查找异常处理器使得执行栈展开时,会在每个展开层级的当前回溯之前插入一个回溯对象。 当进入一个异常处理器时,程序将可以使用栈跟踪。 (参见try 语句 一节。) 它可作为sys.exc_info() 所返回的元组的第三项,以及所捕获异常的__traceback__ 属性被获取。

当程序不包含适用的处理器时,栈跟踪会(以良好的格式)写入到标准错误流;如果解释器处于交互模式,它也将作为sys.last_traceback 供用户使用。

对于显式地创建的回溯对象,则由回溯对象的创建者来决定应该如何连接tb_next 属性以构成完整的线跟踪。

特殊的只读属性:

traceback.tb_frame

指向当前层级的执行帧对象

访问该属性将引发一个审计事件object.__getattr__,附带参数obj"tb_frame"

traceback.tb_lineno

给出异常发生所在的行号

traceback.tb_lasti

表示“精确指令”。

回溯中的行号和最后一条指令可能与其帧对象 的行号不同,如果异常发生在try 语句中且没有匹配的 except 子句或是有finally 子句的话。

traceback.tb_next

特殊的可写属性tb_next 是栈跟踪中的下一层级(通往发生异常的帧),如果没有下一层级则为None

在 3.7 版本发生变更:该属性现在是可写的。

3.2.13.4.切片对象

切片对象被用来表示__getitem__() 方法所使用的切片。 该对象也可使用内置的slice() 函数来创建。

特殊的只读属性:start 为下界;stop 为上界;step 为步长值; 各值如省略则为None。这些属性可具有任意类型。

切片对象支持一个方法:

slice.indices(self,length)

此方法接受一个整型参数length 并计算在切片对象被应用到length 指定长度的条目序列时切片的相关信息应如何描述。 其返回值为三个整型数组成的元组;这些数分别为切片的startstop 索引号以及step 步长值。索引号缺失或越界则按照与正规切片相一致的方式处理。

3.2.13.5.静态方法对象

静态方法对象提供了一种胜过上文所述将函数对象转换为方法对象的方式。 静态方法对象是对任意其他对象的包装器,通常用来包装用户自定义的方法对象。 当从类或类实例获取一个静态方法对象时,实际返回的是经过包装的对象,它不会被进一步转换。 静态方法对象也是可调用对象。 静态方法对象可通过内置的staticmethod() 构造器来创建。

3.2.13.6.类方法对象

类方法对象与静态方法类似,是对其他对象的包装器,会改变从类或类实例获取该对象的方式。 类方法对象在这种获取操作中的行为已在上文中描述,见"实例方法" 一节。 类方法对象是通过内置classmethod() 构造器创建的。

3.3.特殊方法名称

一个类可以通过定义具有特殊名称的方法来实现由特殊语法来唤起的特定操作(例如算术运算或抽取与切片)。 这是 Python 实现运算符重载 的方式,允许每个类自行定义基于该语言运算符的特定行为。 举例来说,如果一个类定义了名为__getitem__() 的方法,并且x 是该类的一个实例,则x[i] 基本就等价于type(x).__getitem__(x,i)。 除非有说明例外情况,在没有定义适当方法的时候尝试执行某种操作将引发一个异常 (通常为AttributeErrorTypeError)。

将一个特殊方法设为None 表示对应的操作不可用。 例如,如果一个类将__iter__() 设为None,则该类就是不可迭代的,因此对其实例调用iter() 将引发一个TypeError (而不会回退至__getitem__())。[2]

在实现模拟任何内置类型的类时,很重要的一点是模拟的实现程度对于被模拟对象来说应当是有意义的。例如,提取单个元素的操作对于某些序列来说是适宜的,但提取切片可能就没有意义。(这种情况的一个实例是 W3C 的文档对象模型中的NodeList 接口。)

3.3.1.基本定制

object.__new__(cls[,...])

调用以创建一个cls 类的新实例。__new__() 是一个静态方法 (因为是特例所以你不需要显式地声明),它会将所请求实例所属的类作为第一个参数。其余的参数会被传递给对象构造器表达式 (对类的调用)。__new__() 的返回值应为新对象实例 (通常是cls 的实例)。

典型的实现会附带适当的参数使用super().__new__(cls[,...]) 通过唤起超类的__new__() 方法来创建一个新的类实例然后在返回它之前根据需要修改新创建的实例。

如果__new__() 在构造对象期间被唤起并且它返回了一个cls 的实例,则新实例的__init__() 方法将以__init__(self[,...]) 的形式被唤起,其中self 为新实例而其余的参数与被传给对象构造器的参数相同。

如果__new__() 未返回一个cls 的实例,则新实例的__init__() 方法就不会被执行。

__new__() 的目的主要是允许不可变类型的子类 (例如 int, str 或 tuple) 定制实例创建过程。它也常会在自定义元类中被重载以便定制类创建过程。

object.__init__(self[,...])

在实例 (通过__new__()) 被创建之后,返回调用者之前调用。其参数与传递给类构造器表达式的参数相同。一个基类如果有__init__() 方法,则其所派生的类如果也有__init__() 方法,就必须显式地调用它以确保实例基类部分的正确初始化;例如:super().__init__([args...]).

因为对象是由__new__()__init__() 协作构造完成的 (由__new__() 创建,并由__init__() 定制),所以__init__() 返回的值只能是None,否则会在运行时引发TypeError

object.__del__(self)

在实例将被销毁时调用。 这还被称为终结器或析构器(不适当)。 如果一个基类具有__del__() 方法,则其所派生的类如果也有__del__() 方法,就必须显式地调用它以确保实例基类部分的正确清除。

__del__() 方法可以 (但不推荐!) 通过创建一个该实例的新引用来推迟其销毁。这被称为对象重生__del__() 是否会在重生的对象将被销毁时再次被调用是由具体实现决定的 ;当前的CPython 实现只会调用一次。

当解释器退出时并不保证会为仍然存在的对象调用__del__() 方法。weakref.finalize 提供了一种直观的方式来注册当对象被作为垃圾回收时要调用的清理函数。

备注

delx 并不直接调用x.__del__() --- 前者会将x 的引用计数减一,而后者仅会在x 的引用计数变为零时被调用。

一个引用循环可以阻止对象的引用计数归零。 在这种情况下,循环将稍后被检测到并被循环垃圾回收器 删除。 导致引用循环的一个常见原因是当一个异常在局部变量中被捕获。 帧的局部变量将会引用该异常,这将引用它自己的回溯信息,它会又引用在回溯中捕获的所有帧的局部变量。

参见

gc 模块的文档。

警告

由于调用__del__() 方法时周边状况已不确定,在其执行期间发生的异常将被忽略,改为打印一个警告到sys.stderr。特别地:

  • __del__() 可在任意代码被执行时启用,包括来自任意线程的代码。如果__del__() 需要接受锁或启用其他阻塞资源,可能会发生死锁,例如该资源已被为执行__del__() 而中断的代码所获取。

  • __del__() 可以在解释器关闭阶段被执行。因此,它需要访问的全局变量(包含其他模块)可能已被删除或设为None。Python 会保证先删除模块中名称以单个下划线打头的全局变量再删除其他全局变量;如果已不存在其他对此类全局变量的引用,这有助于确保导入的模块在__del__() 方法被调用时仍然可用。

object.__repr__(self)

repr() 内置函数调用以输出一个对象的“官方”字符串表示。如果可能,这应类似一个有效的 Python 表达式,能被用来重建具有相同取值的对象(只要有适当的环境)。如果这不可能,则应返回形式如<...someusefuldescription...> 的字符串。返回值必须是一个字符串对象。如果一个类定义了__repr__() 但未定义__str__(),则在需要该类的实例的“非正式”字符串表示时也会使用__repr__()

此方法通常被用于调试,因此确保其表示的内容包含丰富信息且无歧义是很重要的。object 类本身提供了一个默认实现。

object.__str__(self)

str(object), 默认的__format__() 实现以及内置函数print() 调用,以生成一个对象的“非正式”或适合打印的字符串表示形式。 返回值必须为一个str 对象。

此方法与object.__repr__() 的不同点在于__str__() 并不预期返回一个有效的 Python 表达式:可以使用更方便或更准确的描述信息。

内置类型object 所定义的默认实现会调用object.__repr__()

object.__bytes__(self)

bytes 调用以生成一个对象的字节串表示形式。 这应当返回一个bytes 对象。object 类本身不提供此方法。

object.__format__(self,format_spec)

通过format() 内置函数、扩展、格式化字符串字面值 的求值以及str.format() 方法调用以生成一个对象的“格式化”字符串表示。format_spec 参数为包含所需格式选项描述的字符串。format_spec 参数的解读是由实现__format__() 的类型决定的,不过大多数类或是将格式化委托给某个内置类型,或是使用相似的格式化选项语法。

请参看格式规格迷你语言 了解标准格式化语法的描述。

返回值必须为一个字符串对象。

应当为由object 类提供的默认实现给出一个空的format_spec 字符串。 它将委托给__str__()

在 3.4 版本发生变更:object 本身的 __format__ 方法如果被传入任何非空字符,将会引发一个TypeError

在 3.7 版本发生变更:object.__format__(x,'') 现在等同于str(x) 而不再是format(str(x),'')

object.__lt__(self,other)
object.__le__(self,other)
object.__eq__(self,other)
object.__ne__(self,other)
object.__gt__(self,other)
object.__ge__(self,other)

以上这些被称为“富比较”方法。运算符号与方法名称的对应关系如下:x<y 调用x.__lt__(y)x<=y 调用x.__le__(y)x==y 调用x.__eq__(y)x!=y 调用x.__ne__(y)x>y 调用x.__gt__(y)x>=y 调用x.__ge__(y)

如果指定的参数对没有相应的实现,富比较方法可能会返回单例对象NotImplemented 。按照惯例,成功的比较会返回FalseTrue。不过实际上这些方法可以返回任意值,因此如果比较运算符是要用于布尔值判断(例如作为if 语句的条件),Python 会对返回值调用bool() 以确定结果为真还是假。

在默认情况下,object 通过使用is 来实现__eq__() ,并在比较结果为假值时返回NotImplemented :TrueifxisyelseNotImplemented 。 对于__ne__() ,默认会委托给__eq__() 并对结果取反,除非结果为NotImplemented 。 比较运算符之间没有其他隐含关系或默认实现;例如,(x<yorx==y) 为真并不意味着x<=y 。 要根据单根运算自动生成排序操作,请参看functools.total_ordering()

在默认情况下,object 类提供与值比较 一致的实现:相等比较是根据对象标识号,而顺序比较会引发TypeError。 每个默认方法都可能直接生成这样的结果,但也可能返回NotImplemented

请查看__hash__() 的相关段落,了解创建可支持自定义比较运算并可用作字典键的hashable 对象时要注意的一些事项。

这些方法都没有对调参数版本(在左边参数不支持该操作但右边参数支持时使用);而是__lt__()__gt__() 互为对方的反向,__le__()__ge__() 互为对方的反射,而__eq__()__ne__() 则是它们自己的反射。 如果两个操作数的类型不同,且右操作数的类型是左操作数类型的直接或间接子类,则优先选择右操作数的反射方法,在其他情况下优先选择左操作数的方法。 虚拟子类化不会被考虑。

当没有合适的方法返回任何NotImplemented 以外的值时,==!= 运算符将分别回退至isisnot

object.__hash__(self)

通过内置函数hash() 调用以对哈希集的成员进行操作,属于哈希集的类型包括setfrozenset 以及dict__hash__() 应该返回一个整数。对象比较结果相同所需的唯一特征属性是其具有相同的哈希值;建议的做法是把参与比较的对象全部组件的哈希值混在一起,即将它们打包为一个元组并对该元组做哈希运算。例如:

def__hash__(self):returnhash((self.name,self.nick,self.color))

备注

hash() 会从一个对象自定义的__hash__() 方法返回值中截断为Py_ssize_t 的大小。通常对 64 位构建为 8 字节,对 32 位构建为 4 字节。如果一个对象的__hash__() 必须在不同位大小的构建上进行互操作,请确保检查全部所支持构建的宽度。做到这一点的简单方法是使用python-c"importsys;print(sys.hash_info.width)"

如果一个类没有定义__eq__() 方法,那么它也不应该定义__hash__() 操作;如果它定义了__eq__() 但没有定义__hash__(),则其实例将不可被用作可哈希多项集的条目。 如果一个类定义了可变对象并实现了__eq__() 方法,则它不应该实现__hash__(),因为hashable 多项集的实现要求键的哈希值是不可变的(如果对象的哈希值发生改变,它将位于错误的哈希桶中)。

用户自定义的类默认带有__eq__()__hash__() 方法(继承自object 类);因为它们的存在,所有对象(自己除外)相互比较必定不相等并且x.__hash__() 将返回一个恰当的值以使得x==y 同时意味着xisyhash(x)==hash(y)

一个类如果重载了__eq__() 且没有定义__hash__() 则会将其__hash__() 隐式地设为None。当一个类的__hash__() 方法为None 时,该类的实例将在一个程序尝试获取其哈希值时正确地引发TypeError,并会在检测isinstance(obj,collections.abc.Hashable) 时被正确地识别为不可哈希对象。

如果一个重载了__eq__() 的类需要保留来自父类的__hash__() 实现,则必须通过设置__hash__=<ParentClass>.__hash__ 来显式地告知解释器。

如果一个没有重载__eq__() 的类需要去掉哈希支持,则应该在类定义中包含__hash__=None。一个自定义了__hash__() 以显式地引发TypeError 的类会被isinstance(obj,collections.abc.Hashable) 调用错误地识别为可哈希对象。

备注

在默认情况下,str 和 bytes 对象的__hash__() 值会使用一个不可预知的随机值“加盐”。 虽然它们在一个单独 Python 进程中会保持不变,但它们的值在重复运行的 Python 间是不可预测的。

这是为了防止通过精心选择输入来利用字典插入操作在最坏情况下的执行效率即O(n2) 复杂度制度的拒绝服务攻击。 请参阅http://ocert.org/advisories/ocert-2011-003.html 了解详情。

改变哈希值会影响集合的迭代次序。Python 也从不保证这个次序不会被改变(通常它在 32 位和 64 位构建上是不一致的)。

另见PYTHONHASHSEED.

在 3.3 版本发生变更:默认启用哈希随机化。

object.__bool__(self)

调用此方法以实现真值检测以及内置的bool() 操作;应当返回FalseTrue。 当未定义此方法时,则会在定义了__len__() 的情况下调用它,如果其结果不为零则该对象将被视为具有真值。 如果一个类的__len__()__bool__() 均未定义(这也是object 类本身的情况),则其所有实例都将被视为具有真值。

3.3.2.自定义属性访问

可以定义下列方法来自定义对类实例属性访问(x.name 的使用、赋值或删除)的具体含义.

object.__getattr__(self,name)

当默认属性访问因引发AttributeError 而失败时被调用 (可能是调用__getattribute__() 时由于name 不是一个实例属性或self 的类层级树中的属性而引发了AttributeError;或者是由于name 特征属性的__get__() 引发了AttributeError)。 此方法应当返回(找到的)属性值或是引发一个AttributeError 异常。object 类本身没有提供此方法。

请注意如果属性是通过正常机制找到的,则__getattr__() 不会被调用。 (这是在__getattr__()__setattr__() 之间故意设置的不对称性。) 这既是出于执行效率理由也是因为不这样做的话__getattr__() 将无法访问实例的其他属性。 要注意至少对于实例变量来说,你不必在实例属性字典中插入任何值(而是通过插入到其他对象)就可以实现对它的完全控制。 请参阅下面的__getattribute__() 方法了解真正获取对属性访问的完全控制权的办法。

object.__getattribute__(self,name)

此方法会无条件地被调用以实现对类实例属性的访问。如果类还定义了__getattr__(),则后者不会被调用,除非__getattribute__() 显式地调用它或是引发了AttributeError。此方法应当返回(找到的)属性值或是引发一个AttributeError 异常。为了避免此方法中的无限递归,其实现应该总是调用具有相同名称的基类方法来访问它所需要的任何属性,例如object.__getattribute__(self,name)

备注

此方法在作为通过特定语法或内置函数 隐式地调用的结果的情况下查找特殊方法时仍可能会被跳过。 参见特殊方法查找

对于特定的敏感属性访问,引发一个审计事件object.__getattr__,附带参数objname

object.__setattr__(self,name,value)

此方法在一个属性被尝试赋值时被调用。这个调用会取代正常机制(即将值保存到实例字典)。name 为属性名称,value 为要赋给属性的值。

如果__setattr__() 想要赋值给一个实例属性,它应该调用同名的基类方法,例如object.__setattr__(self,name,value)

对特定敏感属性的赋值,会引发一个审计事件object.__setattr__,附带参数obj,name,value

object.__delattr__(self,name)

类似于__setattr__() 但其作用为删除而非赋值。此方法应该仅在delobj.name 对于该对象有意义时才被实现。

对于特定的敏感属性删除,引发一个审计事件object.__delattr__,附带参数objname

object.__dir__(self)

此方法会在针对相应对象调用dir() 时被调用。 返回值必须为一个可迭代对象。dir() 会把返回的可迭代对象转换为列表并对其排序。

3.3.2.1.自定义模块属性访问

特殊名称__getattr____dir__ 还可被用来自定义对模块属性的访问。模块层级的__getattr__ 函数应当接受一个参数,其名称为一个属性名,并返回计算结果值或引发一个AttributeError。如果通过正常查找即object.__getattribute__() 未在模块对象中找到某个属性,则__getattr__ 会在模块的__dict__ 中查找,未找到时会引发一个AttributeError。如果找到,它会以属性名被调用并返回结果值。

__dir__ 函数应当不接受任何参数,并且返回一个表示模块中可访问名称的字符串可迭代对象。 此函数如果存在,将会重写一个模块中的标准dir() 搜索操作。

想要更细致地自定义模块的行为(设置属性和特性属性等待),可以将模块对象的__class__ 属性设置为一个types.ModuleType 的子类。例如:

importsysfromtypesimportModuleTypeclassVerboseModule(ModuleType):def__repr__(self):returnf'Verbose{self.__name__}'def__setattr__(self,attr,value):print(f'Setting{attr}...')super().__setattr__(attr,value)sys.modules[__name__].__class__=VerboseModule

备注

定义模块的__getattr__ 和设置模块的__class__ 只会影响使用属性访问语法进行的查找 -- 直接访问模块全局变量(不论是通过模块内的代码还是通过对模块全局字典的引用)是不受影响的。

在 3.5 版本发生变更:__class__ 模块属性改为可写。

Added in version 3.7:__getattr____dir__ 模块属性。

参见

PEP 562 - 模块 __getattr__ 和 __dir__

描述用于模块的__getattr____dir__ 函数。

3.3.2.2.实现描述器

下列方法仅当一个包含该方法的类(即所谓描述器 类)的实例出现在一个所有者 类之中的时候才会起作用(该描述器必须在所有者类或它的某个上级类的类字典中)。 在下面的例子中,“属性”是指名称为所有者类的__dict__ 中的特征属性的键名的属性。object 类本身没有实现这些协议。

object.__get__(self,instance,owner=None)

调用此方法以获取所有者类的属性(类属性访问)或该类的实例的属性(实例属性访问)。 可选的owner 参数是所有者类而instance 是被用来访问属性的实例,如果通过owner 来访问属性则返回None

此方法应当返回计算得到的属性值或是引发AttributeError 异常。

PEP 252 指明__get__() 为带有一至二个参数的可调用对象。 Python 自身内置的描述器支持此规格定义;但是,某些第三方工具可能要求必须带两个参数。 Python 自身的__getattribute__() 实现总是会传入两个参数,无论它们是否被要求提供。

object.__set__(self,instance,value)

调用此方法以设置instance 指定的所有者类的实例的属性为新值value

请注意,添加__set__()__delete__() 会将描述器变成“数据描述器”。 更多细节请参阅调用描述器

object.__delete__(self,instance)

调用此方法以删除instance 指定的所有者类的实例的属性。

描述器的实例也可能存在__objclass__ 属性:

object.__objclass__

属性__objclass__ 会被inspect 模块解读为指定此对象定义所在的类(正确设置此属性有助于动态类属性的运行时内省)。 对于可调用对象来说,它可以指明预期或要求提供一个特定类型(或子类)的实例作为第一个位置参数(例如,CPython 会为在 C 中实现的未绑定方法设置此属性)。

3.3.2.3.调用描述器

总的说来,描述器就是具有“绑定行为”的对象属性,其属性访问已被描述器协议中的方法所重载:__get__(),__set__()__delete__()。 如果一个对象定义了以上方法中的任意一个,它就被称为描述器。

属性访问的默认行为是从一个对象的字典中获取、设置或删除属性。例如,a.x 的查找顺序会从a.__dict__['x'] 开始,然后是type(a).__dict__['x'],接下来依次查找type(a) 的上级基类,不包括元类。

但是,如果找到的值是定义了某个描述器方法的对象,则 Python 可能会重载默认行为并转而唤起描述器方法。这具体发生在优先级链的哪个环节则要根据所定义的描述器方法及其被调用的方式来决定。

描述器唤起的开始点是一个绑定a.x。参数的组合方式依a 而定:

直接调用

最简单但最不常见的调用方式是用户代码直接唤起一个描述器方法:x.__get__(a)

实例绑定

如果绑定到一个对象实例,a.x 会被转换为调用:type(a).__dict__['x'].__get__(a,type(a))

类绑定

如果绑定到一个类,A.x 会被转换为调用:A.__dict__['x'].__get__(None,A)

超绑定

类似super(A,a).x 这样的带点号查找将在a.__class__.__mro__ 中搜索紧接在A 之后的基类B 并返回B.__dict__['x'].__get__(a,A)。 如果x 不是描述器,则不加改变地返回它。

对于实例绑定,发起描述器调用的优先级取决于定义了哪些描述器方法。 一个描述器可以定义__get__(),__set__()__delete__() 的任意组合。 如果它没有定义__get__(),则访问属性将返回描述器对象自身,除非对象的实例字典中有相应的属性值。 如果描述器定义了__set__() 和/或__delete__(),则它是一个数据描述器;如果两者均未定义,则它是一个非数据描述器。 通常,数据描述器会同时定义__get__()__set__(),而非数据描述器则只有__get__() 方法。 定义了__get__()__set__() (和/或__delete__()) 的数据描述器总是会重载实例字典中的定义。 与之相对地,非数据描述器则可被实例所重载。

Python 方法(包括用@staticmethod@classmethod 装饰的方法)都是作为非数据描述器来实现的。 因而,实例可以重定义和重写方法。 这允许单个实例获得与相同类的其他实例不一样的行为。

property() 函数是作为数据描述器来实现的。因此实例不能重载特性属性的行为。

3.3.2.4.__slots__

__slots__ 允许我们显式地声明数据成员(如特征属性)并禁止创建__dict____weakref__ (除非是在__slots__ 中显式地声明或是在父类中可用。)

相比使用__dict__ 可以显著节省空间。 属性查找速度也可得到显著的提升。

object.__slots__

这个类变量可赋值为字符串、可迭代对象或由实例使用的变量名组成的字符串序列。__slots__ 会为已声明的变量保留空间并阻止自动为每个实例创建__dict____weakref__

使用__slots__ 的注意事项:

  • 当继承自一个没有__slots__ 的类时,实例的__dict____weakref__ 属性将总是可访问的。

  • 没有__dict__ 变量,实例就不能给未在__slots__ 定义中列出的新变量赋值。 尝试给一个未列出的变量名赋值将引发AttributeError。 如果需要动态地给新变量赋值,则要将'__dict__' 加入到在__slots__ 中声明的字符串序列中。

  • 如果未给每个实例设置__weakref__ 变量,则定义了__slots__ 的类就不支持对其实例的弱引用。 如果需要支持弱引用,则要将'__weakref__' 加入到在__slots__ 中声明的字符串序列中。

  • __slots__ 是通过为每个变量名创建描述器 在类层级上实现的。 因此,类属性不能被用来为通过__slots__ 定义的实例变量设置默认值;否则,类属性将会覆盖描述器赋值。

  • __slots__ 声明的作用不只限于定义它的类。 在父类中声明的__slots__ 在其子类中同样可用。 不过,子类的实例将会获得__dict____weakref__,除非子类也定义了__slots__ (它应当只包含附加 槽位的名称)。

  • 如果一个类定义的位置在某个基类中也有定义,则由基类位置定义的实例变量将不可访问(除非通过直接从基类获取其描述器的方式)。这会使得程序的含义变成未定义。未来可能会添加一个防止此情况的检查。

  • 如果为派生自"variable-length"内置类型int,bytestuple 的类定义了非空的*__slots__* 则将引发TypeError

  • 任何非字符串的iterable 都可以被赋值给__slots__

  • 如果是使用一个字典 来给__slots__ 赋值,则该字典的键将被用作槽位名称。 字典的值可被用来为每个属性提供将被inspect.getdoc() 识别并在and displayed in the output ofhelp() 的输出中显示的文档字符串。

  • __class__ 赋值仅在两个类具有相同的__slots__ 时才会起作用。

  • 带有多槽位父类的多重继承 也是可用的,但仅允许一个父类具有由槽位创建的属性(其他基类必须具有空的槽位布局) —— 违反此规则将引发TypeError

  • 如果将iterator 用于__slots__ 则会为该迭代器的每个值创建一个descriptor。 但是,__slots__ 属性将为一个空迭代器。

3.3.3.自定义类创建

当一个类继承另一个类时,会在这个父类上调用__init_subclass__()。 这样,就使得编写改变子类行为的类成为可能。 这与类装饰器有很密切的关联,但类装饰器只能影响它们所应用的特定类,而__init_subclass__ 则只作用于定义了该方法的类在未来的子类。

classmethodobject.__init_subclass__(cls)

当所在类派生子类时此方法就会被调用。cls 将指向新的子类。如果定义为一个普通实例方法,此方法将被隐式地转换为类方法。

传给一个新类的关键字参数会被传给上级类的__init_subclass__。 为了与其他使用__init_subclass__ 的类兼容,应当去掉需要的关键字参数再将其他参数传给基类,例如:

classPhilosopher:def__init_subclass__(cls,/,default_name,**kwargs):super().__init_subclass__(**kwargs)cls.default_name=default_nameclassAustralianPhilosopher(Philosopher,default_name="Bruce"):pass

object.__init_subclass__ 的默认实现什么都不做,只在带任意参数调用时引发一个错误。

备注

元类提示metaclass 将被其它类型机制消耗掉,并不会被传给__init_subclass__ 的实现。实际的元类(而非显式的提示)可通过type(cls) 访问。

Added in version 3.6.

当一个类被创建时,type.__new__() 会扫描类变量并对其中带有__set_name__() 钩子的对象执行回调。

object.__set_name__(self,owner,name)

在所有者类owner 被创建时自动调用。 此对象已被赋值给该类中的name:

classA:x=C()# 自动调用: x.__set_name__(A, 'x')

如果类变量赋值是在类被创建之后进行的,__set_name__() 将不会被自动调用。 如有必要,可以直接调用__set_name__():

classA:passc=C()A.x=c# 钩子未被调用c.__set_name__(A,'x')# 手动唤起钩子

详情参见创建类对象

Added in version 3.6.

3.3.3.1.元类

默认情况下,类是使用type() 来构建的。类体会在一个新的命名空间内执行,类名会被局部绑定到type(name,bases,namespace) 的结果。

类创建过程可通过在定义行传入metaclass 关键字参数,或是通过继承一个包含此参数的现有类来进行定制。在以下示例中,MyClassMySubclass 都是Meta 的实例:

classMeta(type):passclassMyClass(metaclass=Meta):passclassMySubclass(MyClass):pass

在类定义内指定的任何其他关键字参数都会在下面所描述的所有元类操作中进行传递。

当一个类定义被执行时,将发生以下步骤:

  • 解析 MRO 条目;

  • 确定适当的元类;

  • 准备类命名空间;

  • 执行类主体;

  • 创建类对象。

3.3.3.2.解析 MRO 条目

object.__mro_entries__(self,bases)

如果一个出现在类定义中的基类不是type 的实例,则会在该基类中搜索__mro_entries__() 方法。 如果找到了__mro_entries__() 方法,则在创建类时该基类会被替换为调用__mro_entries__() 的结果。 该方法被调用时将附带传给bases 形参的原始基类元组,并且必须返回一个由将被用来替代该基类的类组成的元组。 返回的元组可能为空:在此情况下,原始基类将被忽略。

参见

types.resolve_bases()

动态地解析不属于type 实例的基类。

types.get_original_bases()

在类被__mro_entries__() 修改之前提取其“原始基类”。

PEP 560

对 typing 模块和泛用类型的核心支持。

3.3.3.3.确定适当的元类

为一个类定义确定适当的元类是根据以下规则:

  • 如果没有基类且没有显式指定元类,则使用type()

  • 如果给出一个显式元类而且不是type() 的实例,则其会被直接用作元类;

  • 如果给出一个type() 的实例作为显式元类,或是定义了基类,则使用最近派生的元类。

最近派生的元类会从显式指定的元类(如果有)以及所有指定的基类的元类(即type(cls))中选取。最近派生的元类应为所有 这些候选元类的一个子类型。如果没有一个候选元类符合该条件,则类定义将失败并抛出TypeError

3.3.3.4.准备类命名空间

一旦确定了适当的元类,就开始准备类的命名空间。 如果元类具有__prepare__ 属性,它将以namespace=metaclass.__prepare__(name,bases,**kwds) 的形式被调用(其中如果存在任何额外关键字参数,则应来自类定义)。__prepare__ 方法应当被实现为类方法__prepare__ 所返回的命名空间会被传入__new__,但是当最终的类对象被创建时该命名空间会被拷贝到一个新的dict 中。

如果元类没有__prepare__ 属性,则类命名空间将初始化为一个空的有序映射。

参见

PEP 3115 - Python 3000 中的元类

引入__prepare__ 命名空间钩子

3.3.3.5.执行类主体

类主体会以(类似于)exec(body,globals(),namespace) 的形式被执行。普通调用与exec() 的关键区别在于当类定义发生于函数内部时,词法作用域允许类主体(包括任何方法)引用来自当前和外部作用域的名称。

但是,即使当类定义发生于函数内部时,在类内部定义的方法仍然无法看到在类作用域层次上定义的名称。类变量必须通过实例的第一个形参或类方法来访问,或者是通过下一节中描述的隐式词法作用域的__class__ 引用。

3.3.3.6.创建类对象

一旦执行类主体完成填充类命名空间,将通过调用metaclass(name,bases,namespace,**kwds) 创建类对象(此处的附加关键字参数与传入__prepare__ 的相同)。

如果类主体中有任何方法引用了__class__super,这个类对象会通过零参数形式的super().__class__ 所引用,这是由编译器所创建的隐式闭包引用。这使用零参数形式的super() 能够正确标识正在基于词法作用域来定义的类,而被用于进行当前调用的类或实例则是基于传递给方法的第一个参数来标识的。

在 CPython 3.6 及之后的版本中,__class__ 单元会作为类命名空间中的__classcell__ 条目被传给元类。 如果存在,它必须被向上传播给type.__new__ 调用,以便能正确地初始化该类。 如果不这样做,在 Python 3.8 中将引发RuntimeError

当使用默认的元类type,或者任何最终会调用type.__new__ 的元类时,以下额外的自定义步骤将在创建类对象之后被唤起:

  1. type.__new__ 方法会收集类命名空间中所有定义了__set_name__() 方法的属性;

  2. 这些__set_name__ 方法将附带所定义的类和指定的属性所赋的名称进行调用;

  3. 在新类基于方法解析顺序所确定的直接父类上调用__init_subclass__() 钩子。

在类对象创建之后,它会被传给包含在类定义中的类装饰器(如果有的话),得到的对象将作为已定义的类绑定到局部命名空间。

当通过type.__new__ 创建新类时,作为命令空间形参提供的对象会被拷贝到一个新的有序映射并丢弃原始对象。 这个新拷贝包装在一个只读代理中,该代理会成为类对象的__dict__ 属性。

参见

PEP 3135 - 新的超类型

描述隐式的__class__ 闭包引用

3.3.3.7.元类的作用

元类的潜在作用非常广泛。已经过尝试的设想包括枚举、日志、接口检查、自动委托、自动特征属性创建、代理、框架以及自动资源锁定/同步等等。

3.3.4.自定义实例及子类检查

以下方法被用来重载isinstance()issubclass() 内置函数的默认行为。

特别地,元类abc.ABCMeta 实现了这些方法以便允许将抽象基类(ABC)作为“虚拟基类”添加到任何类或类型(包括内置类型),包括其他 ABC 之中。

type.__instancecheck__(self,instance)

如果instance 应被视为class 的一个(直接或间接)实例则返回真值。如果定义了此方法,则会被调用以实现isinstance(instance,class)

type.__subclasscheck__(self,subclass)

Return true 如果subclass 应被视为class 的一个(直接或间接)子类则返回真值。如果定义了此方法,则会被调用以实现issubclass(subclass,class)

请注意这些方法的查找是基于类的类型(元类)。它们不能作为类方法在实际的类中被定义。这与基于实例被调用的特殊方法的查找是一致的,只有在此情况下实例本身被当作是类。

参见

PEP 3119 - 引入抽象基类

包括通过__instancecheck__()__subclasscheck__() 来定制isinstance()issubclass() 行为的说明,加入此功能的动机是出于向语言添加抽象基类的场景(参见abc 模块)。

3.3.5.模拟泛型类型

当使用类型标注 时,使用 Python 的方括号标记来形参化 一个generic type 往往会很有用处。 例如,list[int] 这样的标注可以被用来表示一个list 中的所有元素均为int 类型。

参见

PEP 484 —— 类型注解

介绍 Python 中用于类型标注的框架

泛用别名类型

代表形参化泛用类的对象的文档

泛型(Generic),用户自定义泛型typing.Generic

有关如何实现可在运行时被形参化并能被静态类型检查器所识别的泛用类的文档。

一个类通常 只有在定义了特殊的类方法__class_getitem__() 时才能被形参化。

classmethodobject.__class_getitem__(cls,key)

按照key 参数指定的类型返回一个表示泛型类的专门化对象。

当在类上定义时,__class_getitem__() 会自动成为类方法。 因此,当它被定义时没有必要使用@classmethod 来装饰。

3.3.5.1.__class_getitem__ 的目的

__class_getitem__() 的目的是允许标准库泛型类的运行时形参化以更方便地对这些类应用类型提示

要实现可以在运行时被形参化并可被静态类型检查所理解的自定义泛型类,用户应当从已经实现了__class_getitem__() 的标准库类继承,或是从typing.Generic 继承,这个类拥有自己的__class_getitem__() 实现。

标准库以外的类上的__class_getitem__() 自定义实现可能无法被第三方类型检查器如 mypy 所理解。 不建议在任何类上出于类型提示以外的目的使用__class_getitem__()

3.3.5.2.__class_getitem____getitem__

通常,使用方括号语法抽取 一个对象将会调用在该对象的类上定义的__getitem__() 实例方法。 不过,如果被拟抽取的对象本身是一个类,则可能会调用__class_getitem__() 类方法。__class_getitem__() 如果被正确地定义,则应当返回一个GenericAlias 对象。

使用表达式obj[x] 来呈现,Python 解释器会遵循下面这样的过程来确定应当调用__getitem__() 还是__class_getitem__():

frominspectimportisclassdefsubscribe(obj,x):"""返回表达式 'obj[x]' 的结果"""class_of_obj=type(obj)# 如果 obj 所属的类定义了 __getitem__,# 则调用 class_of_obj.__getitem__(obj, x)ifhasattr(class_of_obj,'__getitem__'):returnclass_of_obj.__getitem__(obj,x)# 否则,如果 obj 是一个类并且定义了 __class_getitem__,# 则调用 obj.__class_getitem__(x)elifisclass(obj)andhasattr(obj,'__class_getitem__'):returnobj.__class_getitem__(x)# 否则,引发一个异常else:raiseTypeError(f"'{class_of_obj.__name__}' object is not subscriptable")

在 Python 中,所有的类自身也是其他类的实例。 一个类所属的类被称为该类的metaclass,并且大多数类都将type 类作为它们的元类。type 没有定义__getitem__(),这意味着list[int],dict[str,float]tuple[str,bytes] 这样的表达式都将导致__class_getitem__() 被调用:

>>># list 以 "type" 类作为其元类,与大多数类一样:>>>type(list)<class 'type'>>>>type(dict)==type(list)==type(tuple)==type(str)==type(bytes)True>>># "list[int]" 将调用 "list.__class_getitem__(int)">>>list[int]list[int]>>># list.__class_getitem__ 将返回一个 GenericAlias 对象:>>>type(list[int])<class 'types.GenericAlias'>

然而,如果一个类属于定义了__getitem__() 的自定义元类,则抽取该类可能导致不同的行为。 这方面的一个例子可以在enum 模块中找到:

>>>fromenumimportEnum>>>classMenu(Enum):..."""A breakfast menu"""...SPAM='spam'...BACON='bacon'...>>># 枚举类有一个自定义元类:>>>type(Menu)<class 'enum.EnumMeta'>>>># EnumMeta 定义了 __getitem__,>>># 因此 __class_getitem__ 不会被调用,>>># 并且结果不是一个 GenericAlias 对象:>>>Menu['SPAM']<Menu.SPAM: 'spam'>>>>type(Menu['SPAM'])<enum 'Menu'>

参见

PEP 560 - 对 typing 模块和泛型的核心支持

介绍__class_getitem__(),并指明抽取 在何时会导致__class_getitem__() 而不是__getitem__() 被调用

3.3.6.模拟可调用对象

object.__call__(self[,args...])

此方法会在实例作为一个函数被“调用”时被调用;如果定义了此方法,则x(arg1,arg2,...) 大致可以被转写为type(x).__call__(x,arg1,...)object 类本身没有提供此方法。

3.3.7.模拟容器类型

可以定义下列方法来实现容器对象。object 类本身没有提供它们。 容器通常是序列 (如列表元组) 或者映射 (如字典),但也存在其他的容器表示形式。 前面的一组方法被用来模拟序列或模拟映射;两者的区别在于序列所允许的键应为整数k0<=k<N 其中N 为整个序列或者定义条目范围的slice 对象的长度。 此外还建议让映射提供keys(),values(),items(),get(),clear(),setdefault(),pop(),popitem(),copy() 以及update() 等方法,它们的行为应与 Python 的标准字典 对象的类似。collections.abc 模块提供了一个MutableMappingabstract base class 以根据由__getitem__(),__setitem__(),__delitem__()keys() 组成的基本集来创建这些方法。 可变序列应当提供append(),count(),index(),extend(),insert(),pop(),remove(),reverse()sort() 等方法,就像 Python 标准list 对象那样。 最后,序列类型还应当通过定义下文描述的__add__(),__radd__(),__iadd__(),__mul__(),__rmul__()__imul__() 等方法来实现加法(即拼接)和乘法(即重复);它们不应定义其他数值运算符。 此外还建议映射和序列都实现__contains__() 方法以允许高效地使用in 运算符;对于映射,in 应当搜索映射的键;对于序列,则应当搜索其中的值。 另外还建议映射和序列都实现__iter__() 方法以允许高效地迭代容器;对于映射,__iter__() 应当迭代对象的键;对于序列,则应当迭代其中的值。

object.__len__(self)

调用此方法以实现内置函数len()。 应该返回对象的长度,以一个>= 0 的整数表示。 此外,如果一个对象未定义__bool__() 方法而其__len__() 方法返回值为零则它在布尔运算中将被视为具有假值。

在 CPython 中,要求长度最大只能为sys.maxsize。 如果长度大于sys.maxsize 则某些特性 (如len()) 可能会引发OverflowError。 要防止真值测试引发OverflowError,对象必须定义__bool__() 方法。

object.__length_hint__(self)

调用此方法以实现operator.length_hint()。 应该返回对象长度的估计值(可能大于或小于实际长度)。 此长度应为一个>= 0 的整数。 返回值也可以为NotImplemented ,这会被视作与__length_hint__ 方法完全不存在时一样处理。 此方法纯粹是为了优化性能,并不要求正确无误。

Added in version 3.4.

备注

切片是通过下述三个专门方法完成的。以下形式的调用

a[1:2]=b

会为转写为

a[slice(1,2,None)]=b

其他形式以此类推。略去的切片项总是以None 补全。

object.__getitem__(self,key)

调用此方法以实现self[key] 的求值。 对于sequence 类型,接受的键应为整数。 作为可选项,它们也可能支持slice 对象。 对负数索引的支持也是可选项。 如果key 的类型不正确,则可能引发TypeError。 如果key 为序列索引集合范围以外的值(在进行任何负数索引的特殊解读之后),则应当引发IndexError。 对于mapping 类型,如果key 找不到(不在容器中),则应当引发KeyError

备注

for 循环在有不合法索引时会期待捕获IndexError 以便正确地检测到序列的结束。

备注

抽取 一个class 时,可能会调用特殊类方法__class_getitem__() 而不是__getitem__()。 请参阅__class_getitem__ 与 __getitem__ 了解详情。

object.__setitem__(self,key,value)

调用此方法以实现向self[key] 赋值。注意事项与__getitem__() 相同。为对象实现此方法应该仅限于需要映射允许基于键修改值或添加键,或是序列允许元素被替换时。不正确的key 值所引发的异常应与__getitem__() 方法的情况相同。

object.__delitem__(self,key)

调用此方法以实现self[key] 的删除。注意事项与__getitem__() 相同。为对象实现此方法应该权限于需要映射允许移除键,或是序列允许移除元素时。不正确的key 值所引发的异常应与__getitem__() 方法的情况相同。

object.__missing__(self,key)

此方法由dict.__getitem__() 在找不到字典中的键时调用以实现 dict 子类的self[key]

object.__iter__(self)

此方法会在需要为一个容器创建iterator 时被调用。 此方法应当返回一个新的迭代器对象,它可以对容器中的所有对象执行迭代。 对于映射,它应当对窗口中的键执行迭代。

object.__reversed__(self)

此方法(如果存在)会被reversed() 内置函数调用以实现逆向迭代。它应当返回一个新的以逆序逐个迭代容器内所有对象的迭代器对象。

如果未提供__reversed__() 方法,则reversed() 内置函数将回退到使用序列协议 (__len__()__getitem__())。支持序列协议的对象应当仅在能够提供比reversed() 所提供的实现更高效的实现时才提供__reversed__() 方法。

成员检测运算符 (innotin) 通常以对容器进行逐个迭代的方式来实现。 不过,容器对象可以提供以下特殊方法并采用更有效率的实现,这样也不要求对象必须为可迭代对象。

object.__contains__(self,item)

调用此方法以实现成员检测运算符。如果itemself 的成员则应返回真,否则返回假。对于映射类型,此检测应基于映射的键而不是值或者键值对。

对于未定义__contains__() 的对象,成员检测将首先尝试通过__iter__() 进行迭代,然后再使用__getitem__() 的旧式序列迭代协议,参看语言参考中的相应部分

3.3.8.模拟数字类型

定义以下方法即可模拟数字类型。特定种类的数字不支持的运算(例如非整数不能进行位运算)所对应的方法应当保持未定义状态。

object.__add__(self,other)
object.__sub__(self,other)
object.__mul__(self,other)
object.__matmul__(self,other)
object.__truediv__(self,other)
object.__floordiv__(self,other)
object.__mod__(self,other)
object.__divmod__(self,other)
object.__pow__(self,other[,modulo])
object.__lshift__(self,other)
object.__rshift__(self,other)
object.__and__(self,other)
object.__xor__(self,other)
object.__or__(self,other)

These methods are called to implement the binary arithmetic operations(+,-,*,@,/,//,%,divmod(),pow(),**,<<,>>,&,^,|). For instance, toevaluate the expressionx+y, wherex is an instance of a class thathas an__add__() method,type(x).__add__(x,y) is called. The__divmod__() method should be the equivalent to using__floordiv__() and__mod__(); it should not be related to__truediv__(). Note that__pow__() should be defined to acceptan optional third argument if the ternary version of the built-inpow()function is to be supported.

如果这些方法中的某一个不支持与所提供参数进行运算,它应该返回NotImplemented

object.__radd__(self,other)
object.__rsub__(self,other)
object.__rmul__(self,other)
object.__rmatmul__(self,other)
object.__rtruediv__(self,other)
object.__rfloordiv__(self,other)
object.__rmod__(self,other)
object.__rdivmod__(self,other)
object.__rpow__(self,other[,modulo])
object.__rlshift__(self,other)
object.__rrshift__(self,other)
object.__rand__(self,other)
object.__rxor__(self,other)
object.__ror__(self,other)

These methods are called to implement the binary arithmetic operations(+,-,*,@,/,//,%,divmod(),pow(),**,<<,>>,&,^,|) with reflected(swapped) operands. These functions are only called if the left operand doesnot support the corresponding operation[3] and the operands are of differenttypes.[4] For instance, to evaluate the expressionx-y, wherey isan instance of a class that has an__rsub__() method,type(y).__rsub__(y,x) is called iftype(x).__sub__(x,y) returnsNotImplemented.

Note that ternarypow() will not try calling__rpow__() (thecoercion rules would become too complicated).

备注

如果右操作数类型为左操作数类型的一个子类,且该子类提供了指定运算的反射方法,则此方法将先于左操作数的非反射方法被调用。 此行为可允许子类重载其祖先类的运算符。

object.__iadd__(self,other)
object.__isub__(self,other)
object.__imul__(self,other)
object.__imatmul__(self,other)
object.__itruediv__(self,other)
object.__ifloordiv__(self,other)
object.__imod__(self,other)
object.__ipow__(self,other[,modulo])
object.__ilshift__(self,other)
object.__irshift__(self,other)
object.__iand__(self,other)
object.__ixor__(self,other)
object.__ior__(self,other)

调用这些方法来实现增强算术赋值 (+=,-=,*=,@=,/=,//=,%=,**=,<<=,>>=,&=,^=,|=)。 这些方法应当尝试原地执行操作 (对self 进行修改) 并返回结果 (结果可以为self 但这并非必须)。 如果某个方法未被定义,或者如果该方法返回NotImplemented,则相应的增强赋值将回退到普通方法。 举例来说,如果x 是一个具有__iadd__() 方法的类的实例,则x+=y 就等价于x=x.__iadd__(y)。 如果__iadd__() 不存在,或者如果x.__iadd__(y) 返回NotImplemented,则将使用x.__add__(y)y.__radd__(x),如同对x+y 求值一样。 在某些情况下,增强赋值可能导致未预期的错误 (参见为什么 a_tuple[i] += ['item'] 会引发异常?),但此行为实际上是数据模型的一部分。

object.__neg__(self)
object.__pos__(self)
object.__abs__(self)
object.__invert__(self)

调用此方法以实现一元算术运算 (-,+,abs()~)。

object.__complex__(self)
object.__int__(self)
object.__float__(self)

调用这些方法以实现内置函数complex(),int()float()。应当返回一个相应类型的值。

object.__index__(self)

调用此方法以实现operator.index() 以及 Python 需要无损地将数字对象转换为整数对象的场合(例如切片或是内置的bin(),hex()oct() 函数)。 存在此方法表明数字对象属于整数类型。 必须返回一个整数。

如果未定义__int__(),__float__()__complex__() 则相应的内置函数int(),float()complex() 将回退为__index__()

object.__round__(self[,ndigits])
object.__trunc__(self)
object.__floor__(self)
object.__ceil__(self)

调用这些方法以实现内置函数round() 以及math 函数trunc(),floor()ceil()。 除了将ndigits 传给__round__() 的情况之外这些方法的返回值都应当是原对象截断为Integral (通常为int)。

The built-in functionint() falls back to__trunc__() if neither__int__() nor__index__() is defined.

在 3.11 版本发生变更:The delegation ofint() to__trunc__() is deprecated.

3.3.9.with 语句上下文管理器

上下文管理器 是一个对象,它定义了在执行with 语句时要建立的运行时上下文。 上下文管理器处理进入和退出所需运行时上下文以执行代码块。 通常使用with 语句(在with 语句 中描述),但是也可以通过直接调用它们的方法来使用。

上下文管理器的典型用法包括保存和恢复各种全局状态,锁定和解锁资源,关闭打开的文件等等。

有关上下文管理器的更多信息,请参阅上下文管理器类型object 类本身不提供上下文管理器方法。

object.__enter__(self)

进入与此对象相关的运行时上下文。with 语句将会绑定这个方法的返回值到as 子句中指定的目标,如果有的话。

object.__exit__(self,exc_type,exc_value,traceback)

退出关联到此对象的运行时上下文。 各个参数描述了导致上下文退出的异常。 如果上下文是无异常地退出的,三个参数都将为None

如果提供了异常,并且希望方法屏蔽此异常(即避免其被传播),则应当返回真值。 否则的话,异常将在退出此方法时按正常流程处理。

请注意__exit__() 方法不应该重新引发被传入的异常,这是调用者的责任。

参见

PEP 343 - "with" 语句

Pythonwith 语句的规范描述、背景和示例。

3.3.10.定制类模式匹配中的位置参数

当在模式中使用类名称时,默认不允许模式中出现位置参数,例如在MyClass 没有特别支持的情况下caseMyClass(x,y) 通常是无效的。 要能使用这样的模式,类必须定义一个__match_args__ 属性。

object.__match_args__

该类变量可以被赋值为一个字符串元组。 当该类被用于带位置参数的类模式时,每个位置参数都将被转换为关键字参数,并使用__match_args__ 中的对应值作为关键字。 缺失此属性就等价于将其设为()

举例来说,如果MyClass.__match_args__("left","center","right") 则意味着caseMyClass(x,y) 就等价于caseMyClass(left=x,center=y)。 请注意模式中参数的数量必须小于等于__match_args__ 中元素的数量;如果前者大于后者,则尝试模式匹配时将引发TypeError

Added in version 3.10.

参见

PEP 634 - 结构化模式匹配

有关 Pythonmatch 语句的规范说明。

3.3.11.模拟缓冲区类型

缓冲区协议 为 Python 对象提供了一种向低层级内存数组暴露高效访问的方式。 该协议是通过内置类型如bytesmemoryview 实现的,还可能由第三方库定义额外的缓冲区类型。

虽然缓冲区类型通常都是用 C 实现的,但用 Python 来实现该协议也是可能的。

object.__buffer__(self,flags)

当从self 请求一个缓冲区时将被调用(例如,从memoryview 构造器)。flags 参数是代表所请求缓冲区的类别的整数,例如这会影响返回的缓冲区是只读还是可写。inspect.BufferFlags 提供了解读旗标的便利方式。 此方法必须返回一个memoryview 对象。

object.__release_buffer__(self,buffer)

当一个缓冲区不再需要时将被调用。buffer 参数是在此之前由__buffer__() 返回的memoryview 对象。 此方法必须释放任何关联到该缓冲区的资源。 此方法应当返回None。 不需要执行任何清理的缓冲区对象不要求实现此方法。

Added in version 3.12.

参见

PEP 688 - 使缓冲区协议在 Python 中可访问

引入 Python__buffer____release_buffer__ 方法。

collections.abc.Buffer

缓冲区类型的 ABC。

3.3.12.特殊方法查找

对于自定义类来说,特殊方法的隐式唤起仅保证在其定义于对象类型中时能正确地发挥作用,而不能定义在对象实例字典中。 该行为就是以下代码会引发异常的原因。:

>>>classC:...pass...>>>c=C()>>>c.__len__=lambda:5>>>len(c)Traceback (most recent call last):  File"<stdin>", line1, in<module>TypeError:object of type 'C' has no len()

此行为背后的原理在于包括类型对象在内的所有对象都会实现的几个特殊方法如__hash__()__repr__()。 如果这些方法的隐式查找使用了传统的查找过程,则当它们在针对类型对象自身被唤起时将会失败:

>>>1.__hash__()==hash(1)True>>>int.__hash__()==hash(int)Traceback (most recent call last):  File"<stdin>", line1, in<module>TypeError:descriptor '__hash__' of 'int' object needs an argument

以这种方式不正确地尝试唤起一个类的未绑定方法有时被称为‘元类混淆’,可以通过在查找特殊方法时绕过实例的方式来避免:

>>>type(1).__hash__(1)==hash(1)True>>>type(int).__hash__(int)==hash(int)True

除了出于正确性考虑而会绕过任何实例属性,隐式特殊方法查找通常还会绕过__getattribute__() 方法,甚至包括对象的元类:

>>>classMeta(type):...def__getattribute__(*args):...print("Metaclass getattribute invoked")...returntype.__getattribute__(*args)...>>>classC(object,metaclass=Meta):...def__len__(self):...return10...def__getattribute__(*args):...print("Class getattribute invoked")...returnobject.__getattribute__(*args)...>>>c=C()>>>c.__len__()# 通过实例的显式查找Class getattribute invoked10>>>type(c).__len__(c)# 通过类型的显式查找Metaclass getattribute invoked10>>>len(c)# 隐式查找10

以这种方式绕过__getattribute__() 机制为解释器内部的速度优化提供了显著的空间,其代价则是牺牲了一些处理特殊方法时的灵活性(特殊方法must 必须设置在类对象自身上以便始终一致地由解释器唤起)。

3.4.协程

3.4.1.可等待对象

awaitable 对象主要实现了__await__() 方法。 从asyncdef 函数返回的协程对象 即为可等待对象。

备注

从带有types.coroutine() 装饰器的生成器返回的generator iterator 对象也属于可等待对象,但它们并未实现__await__()

object.__await__(self)

必须返回一个iterator。 应当被用来实现awaitable 对象。 例如,asyncio.Future 实现了此方法以与await 表达式兼容。object 类本身不是可等待对象因而不提供此方法。

备注

本语言不会对__await__ 所返回的迭代器产生的对象的类型或值施加任何限制,因为这是负责管理awaitable 对象的异步执行框架的具体实现 (如asyncio) 专属特性。

Added in version 3.5.

参见

PEP 492 了解有关可等待对象的详细信息。

3.4.2.协程对象

协程对象 属于awaitable 对象。 协程的执行可以通过调用__await__() 并迭代其结果来控制。 当协程结束执行并返回时,迭代器会引发StopIteration,而该异常的value 属性将存放返回值。 如果协程引发了异常,它会被迭代器传播出去。 协程不应当直接引发未被处理的StopIteration 异常。

协程也具有下面列出的方法,它们类似于生成器的对应方法 (参见生成器-迭代器的方法)。 但是,与生成器不同,协程并不直接支持迭代。

在 3.5.2 版本发生变更:等待一个协程超过一次将引发RuntimeError

coroutine.send(value)

开始或恢复协程的执行。 如果valueNone,这将等价于前往__await__() 所返回的迭代器的下一项。 如果value 不为None,此方法将委托给导致协挂起的迭代器的send() 方法。 其结果(返回值,StopIteration 或是其他异常)将与上述对__await__() 返回值进行迭代的结果相同。

coroutine.throw(value)
coroutine.throw(type[,value[,traceback]])

在协程内引发指定的异常。 此方法将委托给导致该协程挂起的迭代器的throw() 方法,如果存在此方法的话。 否则,该异常将在挂起点被引发。 其结果(返回值,StopIteration 或是其他异常)将与上述对__await__() 返回值进行迭代的结果相同。 如果该异常未在协程内被捕获,则将回传给调用方。

在 3.12 版本发生变更:第二个签名 (type[, value[, traceback]]) 已被弃用并可能在未来的 Python 版本中移除。

coroutine.close()

此方法会使得协程清理自身并退出。 如果协程被挂起,此方法会先委托给导致协程挂起的迭代器的close() 方法,如果存在该方法。 然后它会在挂起点引发GeneratorExit,使得协程立即清理自身。 最后,协程会被标记为已结束执行,即使它根本未被启动。

当协程对象将要被销毁时,会使用以上处理过程来自动关闭。

3.4.3.异步迭代器

异步迭代器 可以在其__anext__ 方法中调用异步代码。

异步迭代器可在asyncfor 语句中使用。

object 类本身不提供这些方法。

object.__aiter__(self)

必须返回一个异步迭代器 对象。

object.__anext__(self)

必须返回一个可等待对象 输出迭代器的下一结果值。 当迭代结束时应该引发StopAsyncIteration 错误。

异步可迭代对象的一个示例:

classReader:asyncdefreadline(self):...def__aiter__(self):returnselfasyncdef__anext__(self):val=awaitself.readline()ifval==b'':raiseStopAsyncIterationreturnval

Added in version 3.5.

在 3.7 版本发生变更:在 Python 3.7 之前,__aiter__() 可以返回一个可等待对象 并将被解析为异步迭代器

从 Python 3.7 开始,__aiter__() 必须返回一个异步迭代器对象。 返回任何其他对象都将导致TypeError 错误。

3.4.4.异步上下文管理器

异步上下文管理器上下文管理器 的一种,它能够在其__aenter____aexit__ 方法中暂停执行。

异步上下文管理器可在asyncwith 语句中使用。

object 类本身不提供这些方法。

object.__aenter__(self)

在语义上类似于__enter__(),仅有的区别在于它必须返回一个可等待对象

object.__aexit__(self,exc_type,exc_value,traceback)

在语义上类似于__exit__(),仅有的区别在于它必须返回一个可等待对象

异步上下文管理器类的一个示例:

classAsyncContextManager:asyncdef__aenter__(self):awaitlog('entering context')asyncdef__aexit__(self,exc_type,exc,tb):awaitlog('exiting context')

Added in version 3.5.

备注

[1]

在某些情况下有可能 基于可控的条件改变一个对象的类型。 但这通常不是个好主意,因为如果处理不当会导致一些非常怪异的行为。

[2]

__hash__(),__iter__(),__reversed__(),__contains__(),__class_getitem__()__fspath__() 方法对此有特殊处理。 其他方法仍然会引发TypeError,但可能会依赖None 是不可调用对象的行为来做到这一点。

[3]

这里的“不支持”是指该类无此方法,或方法返回NotImplemented 。 如果你想强制回退到右操作数的反射方法,请不要设置方法为None — 那会造成显式地阻塞 此种回退的相反效果。

[4]

For operands of the same type, it is assumed that if the non-reflectedmethod -- such as__add__() -- fails then the overalloperation is notsupported, which is why the reflected method is not called.