Skip to main content

3. 数据模型

3.1. 对象,值和类型

Objects 是Python对数据的抽象。 Python程序中的所有数据都由对象或对象之间的关系表示。 (在某种意义上,并且符合冯·诺依曼的“存储程序计算机”的模型,代码也由对象表示)。

每个对象都有一个标识,一个类型和一个值。对象的 identity 在创建后从不改变;你可能会认为它是内存中的对象地址。 “ is ”运算符比较两个对象的身份; id() 函数返回一个表示其身份的整数。

对于CPython,id(x) 是存储 x 的内存地址。

对象的类型确定对象支持的操作(例如,“它有长度吗?”),并且还定义该类型对象的可能值。 type() 函数返回一个对象的类型(它是一个对象本身)。像它的身份,对象的 type 也是不可改变的。 [1]

一些对象的 value 可以改变。其值可以改变的对象称为 mutable;其值在创建后不可更改的对象称为 immutable。 (包含对可变对象的引用的不可变容器对象的值可以在后者的值被改变时改变;但是容器仍然被认为是不可变的,因为它包含的对象的集合不能被改变,因此,不可变性不是严格的与具有不可改变的值相同,它是更微妙的。)对象的可变性由它的类型决定;例如,数字,字符串和元组是不可变的,而字典和列表是可变的。

对象从未被明确地销毁;然而,当它们变得不可达时,它们可能被垃圾收集。一个实现允许推迟垃圾收集或完全忽略它 - 这是一个实现质量如何实现垃圾收集的问题,只要没有收集仍然可达的对象。

CPython目前使用带有循环链接垃圾的(可选)延迟检测的引用计数方案,它会在大多数对象变得无法访问时收集大多数对象,但不能保证收集包含循环引用的垃圾。有关控制循环垃圾收集的信息,请参阅 gc 模块的文档。其他实现的行为不同,CPython可能会改变。当它们变得不可达时,不要依赖对象的立即结束(因此你应该总是明确地关闭文件)。

请注意,使用实现的跟踪或调试工具可能会使对象活动,通常是可收集的。还要注意,使用“ try ... except ”语句捕获异常可能会使对象活动。

某些对象包含对“外部”资源的引用,例如打开的文件或窗口。应当理解,当对象被垃圾回收时,这些资源被释放,但是由于垃圾回收不能保证发生,这样的对象也提供了一种释放外部资源的显式方法,通常是 close() 方法。强烈建议程序明确关闭此类对象。 ‘try ... finally‘语句和’with‘语句提供了方便的方法。

一些对象包含对其他对象的引用;这些称为 containers。容器的例子是元组,列表和字典。引用是容器值的一部分。在大多数情况下,当我们谈论容器的值时,我们意味着值,而不是所包含对象的身份;然而,当我们谈论容器的可变性时,只隐含立即包含的对象的标识。因此,如果一个不可变容器(像一个元组)包含一个对可变对象的引用,如果可变对象被改变,它的值会改变。

类型几乎影响对象行为的所有方面。即使对象身份的重要性在某种意义上受到影响:对于不可变类型,计算新值的操作实际上可能返回对具有相同类型和值的任何现有对象的引用,而对于可变对象,这是不允许的。例如,在 a = 1; b = 1 之后,ab 可能或可能不会引用具有值1的相同对象,这取决于实现,但是在 c = []; d = [] 之后,cd 被保证涉及两个不同的,唯一的新创建的空列表。 (请注意,c = d = []cd 分配相同的对象。)

3.2. 标准类型层次结构

下面是Python中内置的类型列表。扩展模块(用C,Java或其他语言编写,取决于实现)可以定义其他类型。 Python的未来版本可以向类型层次结构(例如,有理数,有效存储的整数数组等)添加类型,尽管这种添加通常通过标准库来提供。

下面的一些类型描述包含列出“特殊属性”的段落。这些属性提供对实现的访问并且不用于一般使用。他们的定义可能会在将来改变。

没有

此类型具有单个值。有一个具有此值的单个对象。此对象通过内置名称 None 访问。它用于表示在许多情况下不存在值,例如,它从没有显式返回任何东西的函数返回。它的真值是假的。

未实现

此类型具有单个值。有一个具有此值的单个对象。此对象通过内置名称 NotImplemented 访问。如果数值方法和丰富的比较方法不对所提供的操作数实施操作,则应返回此值。 (解释器将尝试反射操作,或一些其他后退,取决于操作符。)其真值为真。

有关详细信息,请参阅 实现算术运算

省略

此类型具有单个值。有一个具有此值的单个对象。此对象通过文字 ... 或内置名称 Ellipsis 访问。它的真值是真的。

numbers.Number

这些是由数字文字创建的,并且作为算术运算符和算术内置函数的结果返回。数字对象是不可变的;一旦创建它们的价值永远不会改变。 Python数字当然与数学数字密切相关,但受计算机中数字表示的限制。

Python区分整数,浮点数和复数:

numbers.Integral

这些表示来自整数的数学集合(正和负)的元素。

有两种类型的整数:

整数(int

这些表示无限范围内的数字,仅受可用(虚拟)内存的限制。为了移位和掩码操作的目的,假设二进制表示,并且负数表示为2的补码的变体,其给出向左延伸的符号位的无限串的错觉。

布尔(bool

这些表示真值False和True。表示值 FalseTrue 的两个对象是唯一的布尔对象。布尔类型是整数类型的子类型,布尔值在几乎所有上下文中分别类似于值0和1,例外情况是,当转换为字符串时,分别返回字符串 "False""True"

整数表示的规则旨在给出涉及负整数的移位和掩码运算的最有意义的解释。

numbers.Realfloat

这些表示机器级双精度浮点数。对于接受的范围和溢出的处理,您处于底层机器架构(以及C或Java实现)的怜悯。 Python不支持单精度浮点数;处理器和内存使用的节省通常是使用这些的原因,因为在Python中使用对象的开销相形见绌,所以没有理由使用两种浮点数使语言复杂化。

numbers.Complexcomplex

这些表示复数作为一对机器级双精度浮点数。同样的注意事项适用于浮点数。复数 z 的实部和虚部可以通过只读属性 z.realz.imag 来检索。

序列

这些表示由非负数索引的有限有序集。内置函数 len() 返回序列的项目数。当序列的长度是 n 时,索引集包含数字0,1,...,n-1。通过 a[i] 选择序列 a 的项目 i

序列还支持切片:a[i:j] 选择具有索引 k 的所有项目,使得 i <= k < j。当用作表达式时,切片是相同类型的序列。这意味着索引集将重新编号,以便从0开始。

一些序列还支持具有第三“步骤”参数的“扩展切片”:a[i:j:k] 选择具有索引 xa 的所有项目,其中 x = i + n*kn >= 0i <= x < j

序列根据它们的可变性来区分:

不可变序列

不可变序列类型的对象在创建后不能更改。 (如果对象包含对其他对象的引用,这些其他对象可以是可变的,可以更改;但是,不可变对象直接引用的对象的集合不能更改)。

以下类型是不可变序列:

字符串

字符串是表示Unicode代码点的值序列。范围 U+0000 - U+10FFFF 中的所有代码点都可以在字符串中表示。 Python没有 char 类型;相反,字符串中的每个代码点都表示为长度为 1 的字符串对象。内置函数 ord() 将代码点从其字符串形式转换为范围 0 - 10FFFF 中的整数; chr()0 - 10FFFF 范围内的整数转换为相应的长度 1 字符串对象。 str.encode() 可以用于使用给定的文本编码将 str 转换为 bytes,并且 bytes.decode() 可以用于实现相反。

元组

元组的项是任意的Python对象。两个或多个项的元组由逗号分隔的表达式列表形成。可以通过将逗号附加到表达式(表达式本身不创建元组,因为括号必须可用于表达式的分组)来形成一个项目(’singleton’)的元组。一个空的元组可以由空的一对括号形成。

字节

一个字节对象是一个不可变数组。这些项是8位字节,由0 <= x <256范围内的整数表示。字节字面值(如 b'abc')和内置函数 bytes() 可用于构造字节对象。此外,字节对象可以通过 decode() 方法解码为字符串。

可变序列

可变序列在创建后可以更改。订阅和切片符号可以用作赋值和 del (删除)语句的目标。

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

列表

列表的项目是任意的Python对象。列表是通过在方括号中放置逗号分隔的表达式列表形成的。 (注意,没有特殊情况需要形成长度为0或1的列表)

字节数组

bytearray对象是一个可变数组。它们由内置的 bytearray() 构造函数创建。除了可变(因此不可缓冲)之外,字节数组提供与不可变字节对象相同的接口和功能。

扩展模块 array 提供了可变序列类型的附加示例,如 collections 模块。

设置类型

这些表示无序的,有限的独特的,不可变的对象集合。因此,它们不能通过任何下标索引。但是,它们可以迭代,内置函数 len() 返回集合中的项目数。集合的常见用法是快速成员资格测试,从序列中删除重复,以及计算数学运算,如交集,并集,差分和对称差分。

对于集合元素,相同的不变性规则适用于字典键。注意,数字类型遵守用于数字比较的正常规则:如果两个数字比较相等(例如,11.0),则它们中只有一个可以包含在集合中。

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

集合

这些表示一个可变集。它们由内置的 set() 构造函数创建,之后可以通过多种方法(如 add())进行修改。

冷冻套

这些代表一个不可变的集合。它们由内置的 frozenset() 构造函数创建。由于frozenset是不可变的和 hashable,它可以再次用作另一个集合的元素,或作为字典键。

映射

这些表示由任意索引集索引的有限对象集合。下标符号 a[k] 从映射 a 中选择由 k 索引的项目;这可以在表达式中使用,并作为分配或 del 语句的目标。内置函数 len() 返回映射中的项目数。

目前有一种单一的固有映射类型:

词典

这些表示由几乎任意值索引的有限对象集合。不能作为键接受的值的唯一类型是包含列表或字典或其他可变类型的值,它们通过值而不是对象标识进行比较,原因是字典的有效实现需要密钥的哈希值保持不变。用于键的数字类型遵守用于数字比较的正常规则:如果两个数字比较相等(例如,11.0),则它们可以可互换地用于索引相同的字典条目。

字典是可变的;它们可以通过 {...} 符号创建(参见 字典显示 部分)。

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

可调用类型

这些是可以应用函数调用操作(参见 呼叫 部分)的类型:

用户定义的函数

用户定义的函数对象由函数定义创建(参见 函数定义 部分)。应该使用包含与函数的形式参数列表相同数量的项的参数列表来调用它。

特殊属性:

属性

含义

 

__doc__

函数文档字符串或 None (如果不可用)不是由子类继承

可写

__name__

函数的名称

可写

__qualname__

函数的 qualified name

3.3 新版功能.

可写

__module__

函数在其中定义的模块的名称,或 None (如果不可用)。

可写

__defaults__

包含具有默认值的那些参数的默认参数值的元组,如果没有参数具有默认值,则为 None

可写

__code__

代码对象表示编译的函数体。

可写

__globals__

对保存函数的全局变量的字典的引用—定义函数的模块的全局命名空间。

只读

__dict__

支持任意函数属性的命名空间。

可写

__closure__

None 或包含函数的自由变量的绑定的单元格的元组。

只读

__annotations__

包含参数注释的dict。 dict的键是参数名称,返回注释的 'return' (如果提供)。

可写

__kwdefaults__

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

可写

大多数标记为“可写”的属性检查分配值的类型。

函数对象还支持获取和设置任意属性,这些属性可用于例如将元数据附加到函数。常规属性点表示法用于获取和设置此类属性。 请注意,当前实现仅支持用户定义函数上的函数属性。将来可能支持内置函数的函数属性。

有关函数定义的附加信息可以从其代码对象中检索;请参阅下面的内部类型的描述。

实例方法

实例方法对象组合了类,类实例和任何可调用对象(通常是用户定义的函数)。

特殊只读属性:__self__ 是类实例对象,__func__ 是函数对象; __doc__ 是方法的文档(与 __func__.__doc__ 相同); __name__ 是方法名(与 __func__.__name__ 相同); __module__ 是方法定义的模块的名称,或 None (如果不可用)。

方法还支持访问(但不设置)底层函数对象上的任意函数属性。

当获取类的属性(可能通过该类的实例)时,如果该属性是用户定义的函数对象或类方法对象,则可以创建用户定义的方法对象。

当一个实例方法对象通过从一个类的实例中检索一个用户定义的函数对象来创建时,它的 __self__ 属性就是实例,并且方法对象被说成是绑定的。新方法的 __func__ 属性是原始函数对象。

当通过从类或实例检索另一个方法对象来创建用户定义的方法对象时,除了新实例的 __func__ 属性不是原始方法对象,而是其 __func__ 属性之外,其行为与对于函数对象的行为相同。

当通过从类或实例检索类方法对象来创建实例方法对象时,它的 __self__ 属性是类本身,其 __func__ 属性是类方法基础的函数对象。

当实例方法对象被调用时,调用底层函数(__func__),在参数列表前面插入类实例(__self__)。例如,当 C 是包含函数 f() 的定义的类,并且 xC 的实例时,调用 x.f(1) 等效于调用 C.f(x, 1)

当实例方法对象从类方法对象派生时,存储在 __self__ 中的“类实例”实际上将是类本身,因此调用 x.f(1)C.f(1) 等效于调用 f(C,1),其中 f 是底层函数。

注意,从函数对象到实例方法对象的转换每次从实例检索属性时发生。在某些情况下,富有成效的优化是将属性分配给局部变量并调用该局部变量。还要注意,这种转换只发生在用户定义的函数上;其他可调用对象(和所有不可调用对象)被检索而没有变换。还需要注意的是,作为类实例的属性的用户定义函数不会转换为绑定方法;当 only 函数是类的一个属性时,就会发生这个 only

发电机功能

使用 yield 语句(参见 yield 声明 部分)的函数或方法称为 generator function。这样的函数,当被调用时,总是返回一个迭代器对象,可以用来执行函数的主体:调用迭代器的 iterator.__next__() 方法将导致函数执行,直到它提供一个值使用 yield 语句。当函数执行 return 语句或者结束时,会引发 StopIteration 异常,并且迭代器将达到要返回的值集合的末尾。

协同功能

使用 async def 定义的函数或方法称为 coroutine function。这样的函数在被调用时返回 coroutine 对象。它可以包含 await 表达式,以及 async withasync for 语句。参见 协同对象 部分。

异步发生器功能

使用 async def 定义并使用 yield 语句的函数或方法称为 asynchronous generator function。这样的函数在被调用时返回一个异步迭代器对象,它可以在 async for 语句中用来执行函数体。

调用异步迭代器的 aiterator.__anext__() 方法将返回一个 awaitable,它在等待时将执行,直到它使用 yield 表达式提供一个值。当函数执行一个空 return 语句或者结束时,会出现一个 StopAsyncIteration 异常,并且异步迭代器将达到要产生的值的集合的末尾。

内置功能

内置函数对象是C函数的包装器。内置函数的示例是 len()math.sin()math 是标准的内置模块)。参数的数量和类型由C函数确定。特殊只读属性:__doc__ 是函数的文档字符串,或 None (如果不可用) __name__ 是函数的名称; __self__ 设置为 None (但参见下一项); __module__ 是函数在其中定义的模块的名称或 None (如果不可用)。

内置方法

这实际上是一个内置函数的不同伪装,这次包含作为隐式额外参数传递给C函数的对象。内置方法的一个示例是 alist.append(),假设 alist 是一个列表对象。在这种情况下,特殊只读属性 __self__ 被设置为由 alist 表示的对象。

类是可调用的。这些对象通常作为自己的新实例的工厂,但是对于覆盖 __new__() 的类类型,可能有变化。调用的参数传递给 __new__(),在典型情况下,传递给 __init__() 以初始化新实例。

类实例

任意类的实例可以通过在其类中定义 __call__() 方法来调用。

模块

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

属性分配更新模块的命名空间字典,例如,m.x = 1 等效于 m.__dict__["x"] = 1

预定义(可写)属性:__name__ 是模块的名称; __doc__ 是模块的文档字符串,如果 None 不可用,则为 None__annotations__ (可选)是包含在模块体执行期间收集的 变量注释 的字典; __file__ 是加载模块的文件的路径名,如果是从文件加载的。对于某些类型的模块,例如静态链接到解释器中的C模块,__file__ 属性可能丢失;对于从共享库动态加载的扩展模块,它是共享库文件的路径名。

特殊只读属性:__dict__ 是模块的命名空间作为字典对象。

由于CPython清除模块字典的方式,模块字典将在模块脱离范围时被清除,即使字典仍然有活动引用。为了避免这种情况,请复制字典或保持模块直接使用其字典。

自定义类

自定义类类型通常由类定义创建(参见 类定义 部分)。类有一个由字典对象实现的命名空间。类属性引用被转换为该字典中的查找,例如,C.x 被转换为 C.__dict__["x"] (尽管存在许多钩子,其允许定位属性的其他手段)。当在那里没有找到属性名称时,属性搜索在基类中继续。这种对基类的搜索使用C3方法解析顺序,即使在存在导致回到共同祖先的多个继承路径的“菱形”继承结构的情况下,它也能正确地工作。 Python使用的C3 MRO的更多细节可以在 https://www.python.org/download/releases/2.3/mro/ 的2.3版本中的文档中找到。

当类属性引用(对于类 C,说)将产生一个类方法对象,它被转换为一个实例方法对象,其 __self__ 属性是 C。当它将产生一个静态方法对象时,它将被转换为由静态方法对象包装的对象。有关从类中检索的属性可能与实际包含在其 __dict__ 中的属性不同的另一种方式,请参见第 实现描述符 节。

类属性赋值更新类的字典,而不是基类的字典。

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

特殊属性:__name__ 是类名; __module__ 是定义类的模块名称; __dict__ 是包含类的命名空间的字典; __bases__ 是包含基类的元组(可能为空或单例),按它们在基类列表中出现的顺序排列; __doc__ 是类的文档字符串,如果未定义,则为 None__annotations__ (可选)是包含在类体执行期间收集的 变量注释 的字典。

类实例

类实例通过调用类对象(见上文)创建。类实例具有实现为字典的命名空间,这是搜索属性引用的第一个位置。当在那里没有找到属性,并且实例的类具有该名称的属性时,搜索继续类属性。如果发现一个类属性是用户定义的函数对象,它将被转换为一个实例方法对象,其 __self__ 属性是实例。静态方法和类方法对象也被转换;见上面“类”。有关通过其实例检索的类的属性可能与实际存储在类的 __dict__ 中的对象不同的另一种方式,请参见 实现描述符 部分。如果没有找到类属性,并且对象的类有一个 __getattr__() 方法,则调用该方法以满足查找。

属性赋值和删除更新实例的字典,从不是类的字典。如果类有 __setattr__()__delattr__() 方法,则调用这个方法,而不是直接更新实例字典。

如果类实例具有某些特殊名称的方法,它们可以伪装成数字,序列或映射。参见 特殊方法名称

特殊属性:__dict__ 是属性字典; __class__ 是该实例的类。

I/O对象(也称为文件对象)

file object 表示打开的文件。可以使用各种快捷方式创建文件对象:open() 内置函数,以及套接字对象的 os.popen()os.fdopen()makefile() 方法(以及可能由扩展模块提供的其他函数或方法)。

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

内部类型

解释器内部使用的几种类型暴露给用户。它们的定义可能随着解释器的未来版本而改变,但是在这里提及它们是为了完整性。

代码对象

代码对象表示 byte-compiled 可执行的Python代码或 bytecode。代码对象和函数对象之间的区别在于,函数对象包含对函数的全局变量(定义它的模块)的显式引用,而代码对象不包含上下文;还将默认参数值存储在函数对象中,而不是在代码对象中(因为它们表示在运行时计算的值)。与函数对象不同,代码对象是不可变的,并且不包含对可变对象的引用(直接或间接)。

特殊只读属性:co_name 给出函数名; co_argcount 是位置参数的数量(包括具有默认值的参数); co_nlocals 是函数使用的局部变量的数量(包括参数); co_varnames 是一个包含局部变量名称(以变量名开头)的元组; co_cellvars 是一个元组,包含嵌套函数引用的局部变量的名称; co_freevars 是一个包含自由变量名称的元组; co_code 是表示字节码指令序列的字符串; co_consts 是包含字节码使用的文字的元组; co_names 是包含字节码使用的名称的元组; co_filename 是编译代码的文件名; co_firstlineno 是函数的第一行数; co_lnotab 是一个字符串编码从字节码偏移到行号的映射(有关详细信息,请参阅解释器的源代码); co_stacksize 是所需的堆栈大小(包括局部变量); co_flags 是用于解释器的多个标志的整数编码。

co_flags 定义以下标志位:如果函数使用 *arguments 语法接受任意数量的位置参数,则设置 0x04 位;如果函数使用 **keywords 语法接受任意关键字参数,则设置 0x08 位;如果该函数是发生器,则设置位 0x20

未来功能声明(from __future__ import division)还使用 co_flags 中的位来指示代码对象是否已使用启用的特定功能进行编译:如果函数是在启用未来除法的情况下编译的,则设置 0x2000 位;位 0x100x1000 在早期版本的Python中使用。

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

如果代码对象表示一个函数,则 co_consts 中的第一个项是函数的文档字符串,如果未定义,则为 None

框架对象

框架对象表示执行框架。它们可能出现在追溯对象中(见下文)。

特殊的只读属性:f_back 是对上一个堆栈帧(向调用者),或 None,如果这是底层堆栈帧; f_code 是在此帧中执行的代码对象; f_locals 是用于查找局部变量的字典; f_globals 用于全局变量; f_builtins 用于内置(内在)名称; f_lasti 给出精确的指令(这是一个索引到代码对象的字节码字符串中)。

特殊可写属性:如果不是 None,则 f_trace 是在每个源代码行开始时调用的函数(这由调试器使用); f_lineno 是帧的当前行号 - 从跟踪函数内对其进行写入跳转到给定行(仅对于最下面的帧)。调试器可以通过写入f_lineno来实现Jump命令(也称为设置下一个语句)。

框架对象支持一种方法:

frame.clear()

此方法清除对框架所持有的局部变量的所有引用。此外,如果帧属于发电机,则发电机被最终确定。这有助于打破涉及框架对象的引用循环(例如捕获异常并存储其回溯以供以后使用时)。

如果帧当前正在执行,则引发 RuntimeError

3.4 新版功能.

跟踪对象

跟踪对象表示异常的堆栈跟踪。发生异常时将创建跟踪对象。当搜索异常处理程序展开执行堆栈时,在每个展开层次,跟踪对象被插入到当前跟踪前面。当输入异常处理程序时,堆栈跟踪对程序可用。 (参见 try 声明 部分)它作为 sys.exc_info() 返回的元组的第三项可访问。当程序不包含合适的处理程序时,堆栈跟踪被写入(很好地格式化)到标准错误流;如果解释器是交互式的,则它也作为 sys.last_traceback 提供给用户。

特殊的只读属性:tb_next 是堆栈跟踪中的下一级(向发生异常的帧),如果没有下一级,则为 Nonetb_frame 指向当前级的执行帧; tb_lineno 给出发生异常的行号; tb_lasti 表示精确指令。如果在没有匹配的except子句或finally子句的 try 语句中发生异常,则回溯中的行号和最后一个指令可能与其帧对象的行号不同。

切片对象

切片对象用于表示 __getitem__() 方法的切片。它们也由内置的 slice() 功能创建。

特殊只读属性:start 是下限; stop 是上限; step 是步长值;如果省略,每个都是 None。这些属性可以具有任何类型。

切片对象支持一种方法:

slice.indices(self, length)

此方法采用单个整数参数 length,并计算关于如果应用于 length 项序列时切片对象将描述的切片的信息。它返回三个整数的元组;分别是 startstop 指数以及切片的 step 或步幅长度。以与规则切片一致的方式处理缺失或超出边界的索引。

静态方法对象

静态方法对象提供了一种方法来消除函数对象到上述方法对象的转换。静态方法对象是围绕任何其他对象(通常是用户定义的方法对象)的包装器。当从类或类实例检索静态方法对象时,实际返回的对象是包装对象,不需要进行任何进一步的变换。静态方法对象本身不是可调用的,虽然它们包装的对象通常是。静态方法对象由内置的 staticmethod() 构造函数创建。

类方法对象

类方法对象,就像一个静态方法对象,是另一个对象的包装,改变从类和类实例检索对象的方式。上面在“用户定义的方法”下描述了类方法对象在这种检索上的行为。类方法对象由内置的 classmethod() 构造函数创建。

3.3. 特殊方法名称

类可以通过使用特殊名称定义方法来实现由特殊语法(如算术运算或下标和切片)调用的某些操作。这是Python的 operator overloading 方法,允许类相对于语言操作符定义自己的行为。例如,如果一个类定义了一个名为 __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__() 是一个静态方法(特殊情况,因此您不需要声明它),它将请求实例的类作为其第一个参数。其余的参数是那些传递给对象构造函数expression(对类的调用)。 __new__() 的返回值应该是新的对象实例(通常是 cls 的实例)。

典型的实现通过使用具有适当参数的 super(currentclass, cls).__new__(cls[, ...]) 调用超类的 __new__() 方法来创建该类的新实例,然后在返回之前根据需要修改新创建的实例。

如果 __new__() 返回 cls 的实例,则新实例的 __init__() 方法将像 __init__(self[, ...]) 一样被调用,其中 self 是新实例,其余参数与传递给 __new__() 的参数相同。

如果 __new__() 不返回 cls 的实例,则不会调用新实例的 __init__() 方法。

__new__() 主要用于允许不可变类型的子类(如int,str或tuple)自定义实例创建。它也通常在自定义元类中被覆盖以自定义类创建。

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

实例创建后(由 __new__())调用,但在它返回给调用者之前调用。参数是传递给类构造函数表达式的参数。如果一个基类有一个 __init__() 方法,派生类的 __init__() 方法(如果有的话)必须显式调用它,以确保实例的基类部分的正确初始化;例如:BaseClass.__init__(self, [args...])

因为 __new__()__init__() 在构造对象(__new__() 来创建它,和 __init__() 以自定义)中一起工作,所以 __init__() 不会返回非 None 值;这样做会导致 TypeError 在运行时被引发。

object.__del__(self)

当实例即将被销毁时调用。这也称为析构函数。如果基类具有 __del__() 方法,则派生类的 __del__() 方法(如果有)必须显式调用它,以确保适当删除实例的基类部分。请注意,__del__() 方法可能(尽管不推荐!)通过创建对它的新引用来推迟对实例的销毁。然后可以在以后删除该新引用时调用它。不能保证当解释器退出时仍然存在的对象调用 __del__() 方法。

注解

del x 不直接调用 x.__del__() —前者将 x 的引用计数减1,后者仅在 x 的引用计数达到零时调用。可以防止对象的引用计数变为零的一些常见情况包括:对象之间的循环引用(例如,双向链表或具有父指针和子指针的树数据结构);引用一个捕获异常的函数的堆栈帧上的对象(存储在 sys.exc_info()[2] 中的追踪保持堆栈帧存活);或对堆栈帧上的对象的引用,在交互模式下引发未处理的异常(存储在 sys.last_traceback 中的追溯保持堆栈帧存活)。第一种情况只能通过明确打破周期来弥补;第二个可以通过在traceback对象不再有用时释放对它的引用来解析,第三个可以通过在 sys.last_traceback 中存储 None 来解析。循环垃圾收集器启用时(默认情况下启用),会检测并清除循环引用(垃圾回收)。有关此主题的更多信息,请参阅 gc 模块的文档。

警告

由于调用 __del__() 方法的不稳定情况,在执行期间发生的异常被忽略,并且警告打印到 sys.stderr。此外,当响应于被删除的模块而调用 __del__() 时(例如,当程序的执行完成时),由 __del__() 方法引用的其他全局变量可能已经被删除或在被删除的过程中(例如,导入机器关闭)。为此,__del__() 方法应该做到维持外部不变量所需的绝对最小值。从版本1.5开始,Python保证其名称以单个下划线开头的全局变量在删除其他全局变量之前从其模块中删除;如果不存在对这样的全局的其他引用,则这可以帮助确保在调用 __del__() 方法时导入的模块仍然可用。

object.__repr__(self)

repr() 内置函数调用以计算对象的“官方”字符串表示。如果可能,这应该看起来像一个有效的Python表达式,可以用于重新创建具有相同的值(给定一个适当的环境)的对象。如果不可能,则应返回 <...some useful description...> 格式的字符串。返回值必须是字符串对象。如果一个类定义了 __repr__() 而不是 __str__(),那么当需要该类的实例的“非正式”字符串表示时,也使用 __repr__()

这通常用于调试,因此重要的是表示是信息丰富和明确的。

object.__str__(self)

str(object) 和内置函数 format()print() 调用以计算对象的“非正式”或可打印的字符串表示。返回值必须是 对象。

此方法与 object.__repr__() 的不同之处在于,没有期望 __str__() 返回有效的Python表达式:可以使用更方便或简明的表示。

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

object.__bytes__(self)

bytes() 调用以计算对象的字节字符串表示。这应该返回一个 bytes 对象。

object.__format__(self, format_spec)

format() 内置函数调用,并通过扩展,评估 格式化的字符串文字str.format() 方法,以产生对象的“格式化”字符串表示。 format_spec 参数是一个字符串,其中包含所需格式选项的说明。 format_spec 参数的解释取决于实现 __format__() 的类型,但是大多数类都将格式化委托给其中一个内置类型,或者使用类似的格式化选项语法。

有关标准格式化语法的说明,请参阅 格式规范Mini-Language

返回值必须是字符串对象。

在 3.4 版更改: 如果传递任何非空字符串,object 的__format__方法本身会产生 TypeError

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() 以确定结果是真还是假。

默认情况下,__ne__() 委派给 __eq__() 并反转结果,除非它是 NotImplemented。比较运算符之间没有其他隐含的关系,例如,(x<y or x==y) 的真实性并不意味着 x<=y。要从单个根操作自动生成排序操作,请参阅 functools.total_ordering()

有关创建支持自定义比较操作并可用作字典键的 hashable 对象的一些重要说明,请参阅 __hash__() 上的段落。

没有这些方法的交换参数版本(当左参数不支持操作但是右参数没有时使用);相反,__lt__()__gt__() 是彼此的反射,__le__()__ge__() 是彼此的反射,而 __eq__()__ne__() 是它们自己的反射。如果操作数是不同类型的,而右操作数类型是左操作数类型的直接或间接子类,则右操作数的反映方法具有优先级,否则左操作数的方法具有优先级。不考虑虚拟子类化。

object.__hash__(self)

由内置函数 hash() 调用,并用于对散列集合(包括 setfrozensetdict)成员的操作。 __hash__() 应该返回一个整数。唯一必需的属性是比较相等的对象具有相同的哈希值;建议以某种方式将也用作比较对象的一部分的对象的组件的散列值混合在一起(例如,使用排他或)。

注解

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

如果一个类没有定义 __eq__() 方法,它不应该定义一个 __hash__() 操作;如果它定义 __eq__() 而不是 __hash__(),那么它的实例将不能用作hashable集合中的项。如果类定义了可变对象并实现了一个 __eq__() 方法,它不应该实现 __hash__(),因为hashable集合的实现要求一个键的哈希值是不可变的(如果对象的哈希值改变,它将在错误的哈希桶中)。

默认情况下,用户定义的类具有 __eq__()__hash__() 方法;与它们,所有对象比较不等(除了自己)和 x.__hash__() 返回一个适当的值,使 x == y 意味着 x is yhash(x) == hash(y)

覆盖 __eq__() 但不定义 __hash__() 的类将使其 __hash__() 隐式设置为 None。当类的 __hash__() 方法是 None 时,当程序尝试检索其哈希值时,该类的实例将产生适当的 TypeError,并且当检查 isinstance(obj, collections.Hashable) 时也将被正确地识别为不可哈希。

如果覆盖 __eq__() 的类需要保留来自父类的 __hash__() 的实现,则必须通过设置 __hash__ = <ParentClass>.__hash__ 来明确地告知解释器。

如果没有覆盖 __eq__() 的类希望抑制哈希支持,它应该在类定义中包括 __hash__ = None。定义其自己的 __hash__() 以显式提出 TypeError 的类将被 isinstance(obj, collections.Hashable) 调用错误地标识为可哈希的。

注解

默认情况下,str,bytes和datetime对象的 __hash__() 值被“salted”,具有不可预测的随机值。尽管它们在单个Python进程中保持不变,但是在重复调用Python之前是不可预测的。

这旨在提供针对由仔细选择的输入引起的拒绝服务的保护,所述仔细选择的输入利用了dict插入的最坏情况性能O(n ^ 2)复杂度。有关详细信息,请参阅 http://www.ocert.org/advisories/ocert-2011-003.html

更改哈希值会影响字典,集合和其他映射的迭代顺序。 Python从来没有保证这种排序(通常在32位和64位版本之间有所不同)。

参见 PYTHONHASHSEED

在 3.3 版更改: 默认情况下启用哈希随机化。

object.__bool__(self)

调用实现真值检测和内置操作 bool();应返回 FalseTrue。当未定义此方法时,将调用 __len__() (如果已定义),并且如果对象的结果为非零,则将其视为true。如果一个类定义了 __len__()__bool__(),它的所有实例都被认为是真的。

3.3.2. 自定义属性访问

可以定义以下方法来定制类实例的属性访问(对 x.name 的使用,分配或删除)的含义。

object.__getattr__(self, name)

当属性查找在通常的位置没有找到属性时调用(即,它不是实例属性,也不是在 self 的类树中找到的)。 name 是属性名称。此方法应返回(计算)属性值或引发 AttributeError 异常。

注意,如果通过正常机制找到属性,则不调用 __getattr__()。 (这是 __getattr__()__setattr__() 之间的有意不对称。)这是为了效率原因,因为否则 __getattr__() 将没有办法访问实例的其他属性。请注意,至少对于实例变量,您可以通过不在实例属性字典中插入任何值(而是将它们插入另一个对象)来伪造总体控制。请参阅下面的 __getattribute__() 方法,以实际获得对属性访问的完全控制。

object.__getattribute__(self, name)

无条件地调用对类的实例实现属性访问。如果类也定义了 __getattr__(),则后者不会被调用,除非 __getattribute__() 明确地调用它或产生一个 AttributeError。此方法应返回(计算)属性值或引发 AttributeError 异常。为了避免此方法中的无限递归,其实现应始终调用具有相同名称的基类方法来访问其所需的任何属性,例如 object.__getattribute__(self, name)

注解

当通过语言语法或内置函数隐式调用的结果查找特殊方法时,此方法仍然可能被绕过。见 特殊方法查找

object.__setattr__(self, name, value)

尝试进行属性分配时调用。这被称为而不是正常的机制(即将值存储在实例字典中)。 name 是属性名称,value 是要分配给它的值。

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

object.__delattr__(self, name)

__setattr__(),但属性删除而不是分配。这只有在 del obj.name 对于对象有意义时才能实现。

object.__dir__(self)

当对对象调用 dir() 时调用。必须返回序列。 dir() 将返回的序列转换为列表并对其排序。

3.3.2.1. 实现描述符

以下方法仅在包含方法(所谓的 descriptor 类)的类的实例出现在 owner 类中时应用(该描述符必须在所有者的类字典中或其父对象之一的类字典中)。在下面的示例中,“属性”是指名称为所有者类“ __dict__ ”中属性的键的属性。

object.__get__(self, instance, owner)

调用以获取所有者类(类属性访问)的属性或该类的实例(实例属性访问)的属性。 owner 始终是所有者类,而 instance 是通过属性访问的实例,或者当通过 owner 访问属性时,None。此方法应返回(计算)属性值或引发 AttributeError 异常。

object.__set__(self, instance, value)

调用以将所有者类的实例 instance 上的属性设置为新值 value

object.__delete__(self, instance)

调用以删除所有者类的实例 instance 上的属性。

object.__set_name__(self, owner, name)

在创建拥有类 owner 时调用。描述符已分配给 name

3.6 新版功能.

属性 __objclass__inspect 模块解释为指定该对象被定义的类(设置它可以有助于运行时自动检查动态类属性)。对于可调用对象,它可能指示给定类型(或子类)的实例是第一个位置参数所期望或需要的(例如,CPython为在C中实现的未绑定方法设置此属性)。

3.3.2.2. 调用描述符

通常,描述符是具有“绑定行为”的对象属性,其属性访问已经被描述符协议中的方法覆盖:__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)

超级绑定

如果 asuper 的实例,则绑定 super(B, obj).m() 搜索 obj.__class__.__mro__ 以立即在 B 之前的基本类 A,然后用调用:A.__dict__['m'].__get__(obj, obj.__class__) 调用描述符。

对于实例绑定,描述符调用的优先级取决于定义哪些描述符方法。描述符可以定义 __get__()__set__()__delete__() 的任何组合。如果它没有定义 __get__(),那么访问该属性将返回描述符对象本身,除非对象的实例字典中有一个值。如果描述符定义 __set__() 和/或 __delete__(),则它是数据描述符;如果它既不定义,则它是非数据描述符。通常,数据描述符定义 __get__()__set__(),而非数据描述符只具有 __get__() 方法。定义了 __set__()__get__() 的数据描述符总是覆盖实例字典中的重定义。相反,非数据描述符可以被实例覆盖。

蟒方法(包括 staticmethod()classmethod())被实现为非数据描述符。因此,实例可以重新定义和覆盖方法。这允许单个实例来获取来自同一类的其他实例不同的行为。

property() 功能被实现为数据描述符。因此,实例不能覆盖属性的行为。

3.3.2.3. __slots__

默认情况下,类的实例具有用于属性存储的字典。这浪费了具有非常少的实例变量的对象的空间。创建大量实例时,空间消耗可能变得非常严重。

可以通过在类定义中定义 __slots__ 来覆盖默认值。 __slots__ 声明采用一系列实例变量,并在每个实例中保留足够的空间以保存每个变量的值。由于不会为每个实例创建 __dict__,因此会保存空间。

object.__slots__

此类变量可以分配一个字符串,可迭代的,或者实例使用的变量名称的字符串序列。 __slots__ 为已声明的变量保留空间,并防止为每个实例自动创建 __dict____weakref__

3.3.2.3.1. 使用 __slots__ 的注意事项
  • 当从没有 __slots__ 的类继承时,该类的 __dict__ 属性将始终可访问,因此子类中的 __slots__ 定义是无意义的。

  • 没有 __dict__ 变量,不能为实例分配未列在 __slots__ 定义中的新变量。尝试分配到未列出的变量名称会引发 AttributeError。如果需要动态分配新变量,则将 '__dict__' 添加到 __slots__ 声明中的字符串序列。

  • 没有每个实例的 __weakref__ 变量,定义 __slots__ 的类不支持对其实例的弱引用。如果需要弱引用支持,则将A '__weakref__' 添加到 __slots__ 声明中的字符串序列。

  • __slots__ 在类级别通过为每个变量名创建描述符(实现描述符)来实现。因此,类属性不能用于设置由 __slots__ 定义的实例变量的默认值;否则,类属性将覆盖描述符分配。

  • __slots__ 声明的操作仅限于定义它的类。因此,子类将具有 __dict__,除非它们也定义 __slots__ (其必须仅包含任何 additional 槽的名称)。

  • 如果一个类定义了一个也在基类中定义的槽,那么由基类槽定义的实例变量是不可访问的(除了直接从基类检索它的描述符)。这使得程序的含义未定义。将来,可以添加检查以防止这种情况。

  • 非空 __slots__ 不适用于从“可变长度”内置类型(如 intbytestuple)派生的类。

  • 任何非字符串可迭代可以分配给 __slots__。也可以使用映射;然而,将来,可以对与每个键相对应的值分配特殊的含义。

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

3.3.3. 自定义类创建

每当一个类继承自另一个类,__init_subclass__ 在该类上被调用。这样,就可以编写改变子类行为的类。这与类装饰器密切相关,但是类装饰器只影响它们应用于的特定类,__init_subclass__ 仅适用于定义方法的类的未来子类。

classmethod object.__init_subclass__(cls)

每当包含类被子类化时,将调用此方法。 cls 是新的子类。如果定义为正常的实例方法,则此方法将隐式转换为类方法。

给予一个新类的关键字参数被传递给父类的 __init_subclass__。为了与使用 __init_subclass__ 的其他类的兼容性,应该取出所需的关键字参数,并将其他的传递给基类,如:

class Philosopher:
    def __init_subclass__(cls, default_name, **kwargs):
        super().__init_subclass__(**kwargs)
        cls.default_name = default_name

class AustralianPhilosopher(Philosopher, default_name="Bruce"):
    pass

默认实现 object.__init_subclass__ 不执行任何操作,但如果使用任何参数调用,则会引发错误。

注解

元类提示 metaclass 被类型机制的其余部分使用,并且永远不会传递给 __init_subclass__ 实现。实际的元类(而不是显式提示)可以作为 type(cls) 访问。

3.6 新版功能.

3.3.3.1. 元类

默认情况下,类是使用 type() 构造的。类体在新的命名空间中执行,类名在本地绑定到 type(name, bases, namespace) 的结果。

类创建过程可以通过在类定义行中传递 metaclass 关键字参数来定制,或者通过从包含这样的参数的现有类继承来定制。在下面的例子中,MyClassMySubclass 都是 Meta 的实例:

class Meta(type):
    pass

class MyClass(metaclass=Meta):
    pass

class MySubclass(MyClass):
    pass

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

当执行类定义时,将执行以下步骤:

  • 确定适当的元类

  • 类名称空间已准备好

  • 类主体被执行

  • 将创建类对象

3.3.3.2. 确定适当的元类

类定义的适当元类确定如下:

  • 如果没有给出基础和明确的元类,则使用 type()

  • 如果给定了显式元类,并且它是 nottype() 的实例,则它被直接用作元类

  • 如果给定 type() 的实例作为显式元类,或者定义基数,则使用最大导出的元类

最导出的元类从明确指定的元类(如果有)和所有指定的基类的元类(即 type(cls))中选择。最导出的元类是这些候选元类的 all 的子类型。如果没有一个候选元类满足该标准,那么类定义将与 TypeError 失败。

3.3.3.3. 准备类命名空间

一旦已经识别了适当的元类,则准备类命名空间。如果元类具有 __prepare__ 属性,它被称为 namespace = metaclass.__prepare__(name, bases, **kwds) (其中附加的关键字参数,如果有的话,来自类定义)。

如果元类没有 __prepare__ 属性,那么类命名空间被初始化为空有序映射。

参见

PEP 3115 - Python中的元类

引入了 __prepare__ 命名空间钩子

3.3.3.4. 执行类体

类主体被执行(大约)为 exec(body, globals(), namespace)。与正常调用 exec() 的主要区别在于,当类定义出现在函数内部时,词法作用域允许类主体(包括任何方法)从当前和外部范围引用名称。

但是,即使类定义出现在函数内部,类中定义的方法仍然看不到在类scope中定义的名称。类变量必须通过实例或类方法的第一个参数,或通过下一节中描述的隐式词法作用域 __class__ 引用来访问。

3.3.3.5. 创建类对象

一旦通过执行类主体填充了类命名空间,通过调用 metaclass(name, bases, namespace, **kwds) 创建类对象(这里传递的附加关键字与传递给 __prepare__ 的关键字相同)。

这个类对象将被 super() 的零参数形式引用。 __class__ 是由编译器创建的隐式闭包引用,如果类体中的任何方法引用 __class__super。这允许 super() 的零参数形式正确地识别基于词法范围定义的类,而用于进行当前调用的类或实例基于传递给该方法的第一个参数来标识。

在CPython 3.6和更高版本中,__class__ 单元作为类命名空间中的 __classcell__ 条目传递到元类。如果存在,则必须将其传播到 type.__new__ 调用,以便正确地初始化该类。如果没有这样做,将导致一个 DeprecationWarning 在Python 3.6和未来的 RuntimeWarning

当使用默认元类 type 或最终调用 type.__new__ 的任何元类时,在创建类对象后将调用以下附加自定义步骤:

  • 首先,type.__new__ 收集定义 __set_name__() 方法的类命名空间中的所有描述符;

  • 第二,所有这些 __set_name__ 方法在被定义的类和该特定描述符的分配名称的情况下被调用;和

  • 最后,在它的方法解析顺序中,在新类的直接父对象上调用 __init_subclass__() 钩子。

在创建类对象之后,它将被传递给类定义中包含的类装饰器(如果有的话),并且生成的对象在本地命名空间中作为定义类绑定。

type.__new__ 创建一个新类时,作为命名空间参数提供的对象被复制到一个新的有序映射,原始对象被丢弃。新副本被包裹在只读代理中,它成为类对象的 __dict__ 属性。

参见

PEP 3135 - 新超

描述隐式 __class__ 闭包引用

3.3.3.6. 元类示例

元类的潜在用途是无限的。已经探讨的一些想法包括日志记录,接口检查,自动委派,自动属性创建,代理,框架和自动资源锁定/同步。

这里是一个元类的示例,它使用 collections.OrderedDict 来记住类变量的定义顺序:

class OrderedClass(type):

    @classmethod
    def __prepare__(metacls, name, bases, **kwds):
        return collections.OrderedDict()

    def __new__(cls, name, bases, namespace, **kwds):
        result = type.__new__(cls, name, bases, dict(namespace))
        result.members = tuple(namespace)
        return result

class A(metaclass=OrderedClass):
    def one(self): pass
    def two(self): pass
    def three(self): pass
    def four(self): pass

>>> A.members
('__module__', 'one', 'two', 'three', 'four')

A 的类定义被执行时,该过程开始于调用元类的 __prepare__() 方法,该方法返回空的 collections.OrderedDict。该映射记录 A 的方法和属性,因为它们在类语句的主体中定义。一旦这些定义被执行,有序字典被完全填充,并且元类的 __new__() 方法被调用。该方法构建新类型,并将有序字典键保存在称为 members 的属性中。

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

以下方法用于覆盖 isinstance()issubclass() 内置函数的默认行为。

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

class.__instancecheck__(self, instance)

如果 instance 应该被视为 class 的(直接或间接)实例,则返回true。如果定义,调用实现 isinstance(instance, class)

class.__subclasscheck__(self, subclass)

如果 subclass 应被视为 class 的(直接或间接)子类,则返回true。如果定义,调用实现 issubclass(subclass, class)

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

参见

PEP 3119 - 引入抽象基类

包括通过 __instancecheck__()__subclasscheck__() 定制 isinstance()issubclass() 行为的规范,以及在向该语言添加抽象基类(参见 abc 模块)的上下文中激发此功能。

3.3.5. 模拟可调用对象

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

当实例被“调用”作为函数时调用;如果定义此方法,则 x(arg1, arg2, ...)x.__call__(arg1, arg2, ...) 的缩写。

3.3.6. 仿真容器类型

可以定义以下方法来实现容器对象。容器通常是序列(例如列表或元组)或映射(如字典),但也可以表示其他容器。第一组方法用于模拟序列或模拟映射;区别在于,对于序列,可允许的键应该是其中 N 是序列的长度的 0 <= k < N 的整数 k,或者是定义了项目范围的片段对象。还建议映射提供类似于Python的标准字典对象的方法 keys()values()items()get()clear()setdefault()pop()popitem()copy()update()collections 模块提供 MutableMapping 抽象基类,以帮助从 __getitem__()__setitem__()__delitem__()keys() 的基本集创建这些方法。可变序列应该提供方法 append()count()index()extend()insert()pop()remove()reverse()sort(),像Python标准列表对象。最后,序列类型应通过定义下述方法 __add__()__radd__()__iadd__()__mul__()__rmul__()__imul__() 来实现加法(意味着连接)和乘法(意味着重复)他们不应该定义其他数值运算符。建议映射和序列实现 __contains__() 方法以允许有效使用 in 运算符;对于映射,in 应该搜索映射的键;对于序列,它应该搜索值。进一步推荐映射和序列实现 __iter__() 方法以允许通过容器的有效迭代;对于映射,__iter__() 应与 keys() 相同;对于序列,它应该遍历这些值。

object.__len__(self)

调用实现内置函数 len()。应返回对象的长度,整数 >= 0.此外,未定义 __bool__() 方法并且其 __len__() 方法返回零的对象在布尔上下文中被视为false。

object.__length_hint__(self)

被称为实施 operator.length_hint()。应返回对象的估计长度(其可以大于或小于实际长度)。长度必须是整数 >= 0.这个方法纯粹是一个优化,从来不需要正确性。

3.4 新版功能.

注解

切片只使用以下三种方法完成。一个电话像

a[1:2] = b

翻译成

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

等等。缺少的切片项目始终用 None 填充。

object.__getitem__(self, key)

被要求实施 self[key] 的评估。对于序列类型,接受的键应该是整数和片段对象。注意,负索引的特殊解释(如果该类希望模拟序列类型)取决于 __getitem__() 方法。如果 key 是不适当的类型,TypeError 可能升起;如果序列的索引集合之外的值(在对负值的任何特殊解释之后),则应该引发 IndexError。对于映射类型,如果 key 丢失(不在容器中),则应抛出 KeyError

注解

for 循环期望对非法索引产生 IndexError,以允许正确检测序列的结束。

object.__missing__(self, key)

dict__getitem__() 调用以在键不在字典中时为dict子类实现 self[key]

object.__setitem__(self, key, value)

被称为实现 self[key] 的分配。与 __getitem__() 相同的注释。如果对象支持对键的值进行更改,或者如果可以添加新键,或者对于序列(如果元素可以替换),则应仅对映射实施此操作。对于不正确的 key 值,应该针对 __getitem__() 方法提出相同的例外。

object.__delitem__(self, key)

被称为实施删除 self[key]。与 __getitem__() 相同的注释。如果对象支持删除键,或者对于序列,如果可以从序列中删除元素,则应该仅针对映射实现此操作。对于不正确的 key 值,应该针对 __getitem__() 方法提出相同的例外。

object.__iter__(self)

当容器需要迭代器时,将调用此方法。这个方法应该返回一个新的迭代器对象,它可以遍历容器中的所有对象。对于映射,它应该遍历容器的键。

迭代器对象也需要实现这个方法;他们被要求返回自己。有关迭代器对象的更多信息,请参阅 迭代器类型

object.__reversed__(self)

由内置的 reversed() 调用(如果存在)以实现反向迭代。它应该返回一个新的迭代器对象,以相反的顺序在容器中的所有对象上进行迭代。

如果未提供 __reversed__() 方法,则内置的 reversed() 将回退到使用序列协议(__len__()__getitem__())。支持序列协议的对象只能提供 __reversed__(),如果它们可以提供比 reversed() 提供的更高效的实现。

成员资格测试运算符(innot in)通常通过序列实现为迭代。然而,容器对象可以提供具有更高效实现的以下特殊方法,这也不需要对象是序列。

object.__contains__(self, item)

调用实现成员资格测试运算符。如果 itemself 中,则返回true,否则返回false。对于映射对象,这应该考虑映射的键而不是值或键 - 项对。

对于没有定义 __contains__() 的对象,成员资格测试首先通过 __iter__() 尝试迭代,然后通过 __getitem__() 尝试旧序列迭代协议,参见 这一节在语言参考中

3.3.7. 模拟数值类型

可以定义以下方法来模拟数值对象。与所实现的特定类型的数字不支持的操作(例如,非整数数字的按位操作)相对应的方法应保持未定义。

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)

调用这些方法来实现二进制算术运算(+-*@///%divmod()pow()**<<>>&^|)。例如,为了评估表达式 x + y,其中 x 是具有 __add__() 方法的类的实例,则调用 x.__add__(y)__divmod__() 方法应该等效于使用 __floordiv__()__mod__();它不应该与 __truediv__() 相关。注意,如果要支持内置 pow() 功能的三元版本,__pow__() 应该被定义为接受可选的第三个参数。

如果其中一个方法不支持使用提供的参数的操作,它应该返回 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)
object.__rlshift__(self, other)
object.__rrshift__(self, other)
object.__rand__(self, other)
object.__rxor__(self, other)
object.__ror__(self, other)

调用这些方法以实现具有反射(交换)操作数的二进制算术运算(+-*@///%divmod()pow()**<<>>&^|)。仅当左操作数不支持相应的操作 [3] 和操作数为不同类型时,才调用这些函数。 [4] 例如,为了评估表达式 x - y,其中 y 是具有 __rsub__() 方法的类的实例,如果 x.__sub__(y) 返回 NotImplemented,则调用 y.__rsub__(x)

注意,三元 pow() 不会尝试调用 __rpow__() (强制规则将变得太复杂)。

注解

如果右操作数类型是左操作数类型的子类,并且该子类提供了操作的反映方法,则该方法将在左操作数的非反射方法之前被调用。此行为允许子类覆盖其祖先的操作。

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)。如果未定义特定方法,则扩充的分配将返回到正常方法。例如,如果 x 是具有 __iadd__() 方法的类的实例,则 x += y 等效于 x = x.__iadd__(y)。否则,考虑 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)
object.__round__(self[, n])

调用来实现内置函数 complex()int()float()round()。应返回适当类型的值。

object.__index__(self)

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

注解

为了具有一个连贯的整数类型类,当定义 __index__() 时,还应该定义 __int__(),并且两者应该返回相同的值。

3.3.8. 与语句上下文管理器

context manager 是定义在执行 with 语句时要建立的运行时上下文的对象。上下文管理器处理用于代码块的执行的期望的运行时上下文的入口和退出。上下文管理器通常使用 with 语句(在 with 声明 节中描述)来调用,但也可以通过直接调用它们的方法来使用。

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

有关上下文管理器的更多信息,请参阅 上下文管理器类型

object.__enter__(self)

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

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

退出与此对象相关的运行时上下文。参数描述导致上下文退出的异常。如果上下文没有异常退出,所有三个参数将是 None

如果提供了异常,并且该方法希望抑制异常(即,防止异常传播),则它应该返回一个真值。否则,在退出此方法时将正常处理异常。

请注意,__exit__() 方法不应重新传递传入的异常;这是呼叫者的责任。

参见

PEP 343 - “with”语句

Python with 语句的规范,背景和示例。

3.3.9. 特殊方法查找

对于自定义类,只有在对象的类型而不是对象的实例字典中定义时,才能保证特殊方法的隐式调用正常工作。这种行为是为什么以下代码引发异常的原因:

>>> class C:
...     pass
...
>>> c = C()
>>> c.__len__ = lambda: 5
>>> len(c)
Traceback (most recent call last):
  File "<stdin>", line 1, 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>", line 1, 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__() 方法,甚至绕过对象的元类:

>>> class Meta(type):
...     def __getattribute__(*args):
...         print("Metaclass getattribute invoked")
...         return type.__getattribute__(*args)
...
>>> class C(object, metaclass=Meta):
...     def __len__(self):
...         return 10
...     def __getattribute__(*args):
...         print("Class getattribute invoked")
...         return object.__getattribute__(*args)
...
>>> c = C()
>>> c.__len__()                 # Explicit lookup via instance
Class getattribute invoked
10
>>> type(c).__len__(c)          # Explicit lookup via type
Metaclass getattribute invoked
10
>>> len(c)                      # Implicit lookup
10

以这种方式绕过 __getattribute__() 机制为解释器中的速度优化提供了显着的空间,代价是处理特殊方法的一些灵活性(对于类对象本身设置特殊方法 must 以便由解释器一致地调用)。

3.4. 协同

3.4.1. 等待对象

awaitable 对象通常实现 __await__() 方法。等待从 async def 函数返回的 Coroutine 对象。

注解

从用 types.coroutine()asyncio.coroutine() 装饰的发电机返回的 generator iterator 对象也是等待的,但它们不实现 __await__()

object.__await__(self)

必须返回 iterator。应该用于实现 awaitable 对象。例如,asyncio.Future 实现此方法以与 await 表达式兼容。

3.5 新版功能.

参见

PEP 492 有关等待对象的其他信息。

3.4.2. 协同对象

Coroutine 对象是 awaitable 对象。可以通过调用 __await__() 并对结果进行迭代来控制协同程序的执行。当协程完成执行并返回时,迭代器提高 StopIteration,异常的 value 属性保存返回值。如果协程提出异常,它将由迭代器传播。协程不应该直接引发未处理的 StopIteration 异常。

协同程序也有下面列出的方法,它们类似于发电机的方法(见 生成器迭代器方法)。然而,不像发电机,协同程序不直接支持迭代。

在 3.5.2 版更改: 它是一个 RuntimeError 等待在协程上不止一次。

coroutine.send(value)

启动或恢复协同程序的执行。如果 valueNone,这相当于推进由 __await__() 返回的迭代器。如果 value 不是 None,此方法委托给迭代器的 send() 方法,导致协同程序暂停。结果(返回值,StopIteration 或其他异常)与上述对 __await__() 返回值进行迭代时相同。

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

引发协程中指定的异常。这个方法委托给迭代器的 throw() 方法,如果它有这样的方法,它会导致协同程序挂起。否则,在挂起点处引发异常。结果(返回值,StopIteration 或其他异常)与对 __await__() 返回值进行迭代时相同,如上所述。如果异常没有在协同程序中捕获,它会传播回调用者。

coroutine.close()

导致协同程序自行清理并退出。如果协程被挂起,这个方法首先委托给迭代器的 close() 方法,如果它有这样一个方法,它会引起协同程序挂起。然后,它在悬浮点提高 GeneratorExit,使协同立即清理自己。最后,协同程序被标记为已完成执行,即使它从未启动。

当它们即将被销毁时,Coroutine对象将使用上述过程自动关闭。

3.4.3. 异步迭代器

异步迭代 能够在其 __aiter__ 实现中调用异步代码,并且 异步迭代器 可以在其 __anext__ 方法中调用异步代码。

异步迭代器可以在 async for 语句中使用。

object.__aiter__(self)

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

object.__anext__(self)

必须返回 awaitable,产生迭代器的下一个值。当迭代结束时应该引发 StopAsyncIteration 错误。

异步可迭代对象的示例:

class Reader:
    async def readline(self):
        ...

    def __aiter__(self):
        return self

    async def __anext__(self):
        val = await self.readline()
        if val == b'':
            raise StopAsyncIteration
        return val

3.5 新版功能.

注解

在 3.5.2 版更改: 从CPython 3.5.2开始,__aiter__ 可以直接返回 异步迭代器。返回 awaitable 对象将产生 PendingDeprecationWarning

在CPython 3.5.x中编写向后兼容代码的推荐方法是继续从 __aiter__ 返回等待任务。如果你想避免PendingDeprecationWarning并保持代码向后兼容,可以使用下面的装饰器:

import functools
import sys

if sys.version_info < (3, 5, 2):
    def aiter_compat(func):
        @functools.wraps(func)
        async def wrapper(self):
            return func(self)
        return wrapper
else:
    def aiter_compat(func):
        return func

例:

class AsyncIterator:

    @aiter_compat
    def __aiter__(self):
        return self

    async def __anext__(self):
        ...

从CPython 3.6开始,PendingDeprecationWarning 将替换为 DeprecationWarning。在CPython 3.7中,从 __aiter__ 返回等待的结果将导致 RuntimeError

3.4.4. 异步上下文管理器

异步上下文管理器 是能够在其 __aenter____aexit__ 方法中暂停执行的 上下文管理器

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

object.__aenter__(self)

这种方法在语义上类似于 __enter__(),只是区别在于它必须返回 awaitable

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

这种方法在语义上类似于 __exit__(),只是区别在于它必须返回 awaitable

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

class AsyncContextManager:
    async def __aenter__(self):
        await log('entering context')

    async def __aexit__(self, exc_type, exc, tb):
        await log('exiting context')

3.5 新版功能.

脚注

[1]

在某些情况下,is 可能在某些受控条件下更改对象的类型。它通常不是一个好主意,虽然,因为它可以导致一些非常奇怪的行为,如果它处理不正确。

[2]

__hash__()__iter__()__reversed__()__contains__() 方法对此有特殊处理;其他人仍然会提高 TypeError,但可以通过依赖 None 不可调用的行为来这样做。

[3]

“不支持”这里意味着该类没有这样的方法,或方法返回 NotImplemented。如果你想强制回退到正确的操作数的反射方法,那么不要将方法设置为 None,否则会产生与显式 blocking 相反的效果。

[4]

对于相同类型的操作数,假设如果非反射方法(例如 __add__())失败,则不支持操作,这就是为什么不调用反映的方法。