3. 数据模型

3.1. 对象、值和类型

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

每个对象都有一个标识、一个类型和一个值。物体的 身份 一旦创建了它,就永远不会更改;您可以将它视为对象在内存中的地址。 'is' 运算符比较两个对象的标识; id() 函数返回表示其标识的整数。

CPython implementation detail: 对CPython来说, id(x) 内存地址在哪里 x 存储。

对象的类型决定了对象支持的操作(例如,“它有长度吗?”)并定义该类型对象的可能值。这个 type() 函数返回对象的类型(对象本身)。就像它的身份一样,一个物体的 type 也是不变的。 1

这个 value 有些物体可以改变。其值可以改变的对象称为 易变的 ;创建后其值不变的对象被调用 不变的 . (包含对可变对象的引用的不可变容器对象的值可以在后者的值更改时更改;但是,容器仍然被认为是不可变的,因为它包含的对象集合不能更改。因此,不可变性并不是严格意义上的不变值,它更微妙。)对象的可变性是由其类型决定的;例如,数字、字符串和元组是不变的,而字典和列表是可变的。

对象永远不会被显式销毁;但是,当它们变得不可访问时,它们可能会被垃圾收集。允许一个实现推迟或完全忽略垃圾收集——只要没有收集到仍然可以访问的对象,那么如何实现垃圾收集就是一个实现质量问题。

CPython implementation detail: cpython目前使用的引用计数方案(可选)延迟检测循环链接的垃圾,它在大多数对象无法访问时立即收集这些对象,但不保证收集包含循环引用的垃圾。参见 gc 用于控制循环垃圾收集的信息模块。其他实现的行为不同,cpython可能会改变。当对象变得不可访问时,不要依赖于对象的即时终结(因此您应该总是显式地关闭文件)。

请注意,使用实现的跟踪或调试工具可以使对象保持活动状态,这通常是可以收集的。还要注意,捕获带有 'try...except' 语句可以使对象保持活动状态。

有些对象包含对 "external" 资源(如打开的文件或窗口)的引用。可以理解,当对象被垃圾收集时,这些资源被释放,但是由于垃圾收集不一定会发生,因此这些对象还提供了一种显式释放外部资源的方法,通常是 close() 方法。强烈建议程序显式关闭此类对象。 'try...finally' statement and the 'with' 语句提供了执行此操作的方便方法。

有些对象包含对其他对象的引用;这些对象称为 容器 . 容器的例子有元组、列表和字典。引用是容器值的一部分。在大多数情况下,当我们谈论容器的值时,我们暗示的是值,而不是所包含对象的标识;然而,当我们谈论容器的可变性时,只暗示了立即包含对象的标识。因此,如果不可变容器(如元组)包含对可变对象的引用,则如果该可变对象发生更改,则其值将更改。

类型几乎影响对象行为的所有方面。甚至对象标识的重要性在某种意义上也会受到影响:对于不可变类型,计算新值的操作实际上可能返回对具有相同类型和值的任何现有对象的引用,而对于可变对象,则不允许这样做。例如,在 a = 1; b = 1ab 可能引用或不引用值为1的同一对象,具体取决于实现,但在 c = []; d = []cd 保证引用两个不同的、唯一的、新创建的空列表。(注意 c = d = [] 将同一对象赋给两个对象 cd

3.2. 标准类型层次结构

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

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

没有

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

NotImplemented

此类型只有一个值。有一个具有此值的对象。通过内置名称访问此对象 NotImplemented . 如果数值方法和富比较方法未实现所提供操作数的操作,则它们应返回此值。(然后,解释器将尝试反射操作或其他一些回退,具体取决于运算符。)不应在布尔上下文中对其求值。

实现算术运算 了解更多详细信息。

在 3.9 版更改: 评价 NotImplemented 在布尔上下文中不推荐使用。虽然它当前的计算结果为true,但它将发出 DeprecationWarning . 它会引起 TypeError 在未来的Python版本中。

省略

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

numbers.Number

它们由数字文本创建,并由算术运算符和算术内置函数作为结果返回。数字对象是不可变的;一旦创建了它们的值,就永远不会改变。当然,python数字与数学数字有很强的关联,但受制于计算机中数字表示的局限性。

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

  • 它们是有效的数字文字,当传递给它们的类构造函数时,会产生一个具有原始数字的值的对象。

  • 如果可能,表示法以10为基数。

  • 不显示前导零,可能小数点前的单个零除外。

  • 尾随零(可能小数点后的单个零除外)不会显示。

  • 只有当数字为负数时,才会显示符号。

python区分整数、浮点数和复数:

numbers.Integral

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

整数有两种类型:

整数 (int

这些表示无限范围内的数字,仅限于可用(虚拟)内存。为了进行移位和屏蔽操作,假设使用二进制表示法,用2的补码变量表示负数,这样会产生一个无限长的符号位串延伸到左边的假象。

布尔值 (bool

这些都代表了真理的价值观——假与真。表示值的两个对象 FalseTrue 是唯一的布尔对象。布尔类型是整数类型的子类型,布尔值在几乎所有上下文中的行为都与值0和1类似,但当转换为字符串时,字符串 "False""True" 分别返回。

整数表示规则旨在对涉及负整数的移位和屏蔽操作给出最有意义的解释。

numbers.Real (float

这些表示机器级双精度浮点数。您接受了底层机器体系结构(和C或Java实现)的作用,以便接受溢出和处理。python不支持单精度浮点数;通常使用这些浮点数的原因是节省了处理器和内存的使用量,而在python中使用对象的开销则相形见绌,因此没有理由用两种浮点数使语言复杂化。

numbers.Complex (complex

它们将复数表示为一对机器级双精度浮点数。同样的警告也适用于浮点数。复数的实部和虚部 z 可以通过只读属性检索 z.realz.imag .

序列

这些表示由非负数索引的有限有序集。内置功能 len() 返回序列的项数。当序列的长度为 n ,索引集包含数字0,1,…, n - 1。项目 i 序列的 a 被选中 a[i] .

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

一些序列还支持使用第三个“step”参数进行“扩展切片”: a[i:j:k] 选择的所有项 a 带索引 x 在哪里? x = i + n*kn >= 0i <= x < j .

序列根据其可变性进行区分:

不可变序列

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

以下类型是不可变序列:

字符串

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

多元组

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

字节

字节对象是不可变数组。项目是8位字节,由0<=x<256范围内的整数表示。字节文本(如 b'abc' )以及内置的 bytes() 构造函数可用于创建字节对象。此外,字节对象可以通过 decode() 方法。

可变序列

可变序列可以在创建后更改。订阅和切片标记可以用作分配的目标,并且 del (删除)语句。

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

列表

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

字节数组

Bytearray对象是可变数组。它们是由内置的 bytearray() 构造函数。字节数组除了是可变的(因此是不可变的),还提供了与不可变的相同的接口和功能。 bytes 物体。

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

集合类型

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

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

当前有两种内部集类型:

集合

它们代表一个可变的集合。它们是由内置的 set() 然后可以通过多种方法进行修改,例如 add() .

冻结集

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

映射

这些表示由任意索引集索引的有限对象集。下标符号 a[k] 选择索引项 k 从地图上 a ;这可以在表达式中使用,也可以作为赋值的目标,或者 del 声明。内置功能 len() 返回映射中的项数。

当前有一个内部映射类型:

辞典

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

字典保留插入顺序,这意味着键将按照在字典上按顺序添加的顺序生成。替换现有密钥不会更改顺序,但是删除密钥并重新插入它会将其添加到末尾,而不是保留其原来的位置。

字典是可变的;它们可以由 {{...}} 符号(见第节 词典显示

扩展模块 dbm.ndbmdbm.gnu 提供映射类型的其他示例,以及 collections 模块。

在 3.7 版更改: 字典在3.6之前的Python版本中没有保留插入顺序。在CPython 3.6中,插入顺序被保留下来,但当时它被认为是一个实现细节,而不是一个语言保证。

可调用类型

这些是函数调用操作的类型(请参见第节 调用 )可以应用:

用户定义函数

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

特殊属性:

属性

意义

__doc__

函数的文档字符串,或 None 如果不可用;不由子类继承。

可写的

__name__

函数的名称。

可写的

__qualname__

函数的 qualified name .

3.3 新版功能.

可写的

__module__

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

可写的

__defaults__

包含具有默认值的参数的默认参数值的元组,或者 None 如果没有参数具有默认值。

可写的

__code__

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

可写的

__globals__

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

只读

__dict__

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

可写的

__closure__

None 或者包含函数自由变量绑定的单元格的元组。有关 cell_contents 属性。

只读

__annotations__

一种包含参数注释的字典。dict的键是参数名,以及 'return' 对于返回注释(如果提供)。

可写的

__kwdefaults__

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

可写的

标记为“可写”的大多数属性都检查指定值的类型。

函数对象还支持获取和设置任意属性,例如,可以使用这些属性将元数据附加到函数。常规属性点表示法用于获取和设置此类属性。 Note that the current implementation only supports function attributes on user-defined functions. Function attributes on built-in functions may be supported in the future.

单元格对象具有属性 cell_contents . 这可以用于获取单元格的值,也可以设置该值。

可以从函数的代码对象中检索有关函数定义的其他信息;请参见下面的内部类型描述。这个 cell 可以在中访问类型 types 模块。

实例方法

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

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

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

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

当实例方法对象通过其实例之一从类中检索用户定义的函数对象而创建时,其 __self__ 属性是实例,方法对象被称为绑定的。新方法的 __func__ 属性是原始函数对象。

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

当调用实例方法对象时,基础函数 (__func__ )调用,插入类实例 (__self__ )在参数列表前面。例如,当 C 是包含函数定义的类 f()x 是的实例 C 呼唤 x.f(1) 等于调用 C.f(x, 1) .

当实例方法对象从类方法对象派生时,“类实例”存储在 __self__ 实际上是类本身,因此调用 x.f(1)C.f(1) 等于调用 f(C,1) 在哪里? f 是基础函数。

注意,每次从实例中检索属性时,都会发生从函数对象到实例方法对象的转换。在某些情况下,有效的优化是将属性赋给局部变量并调用该局部变量。另外请注意,此转换只对用户定义的函数发生;其他可调用对象(以及所有不可调用对象)在不进行转换的情况下进行检索。还需要注意的是,属于类实例属性的用户定义函数不会转换为绑定方法;这 only 当函数是类的属性时发生。

生成器功能

一种使用 yield 声明(见第节 这个 yield 陈述 )被称为 generator function . 当调用此类函数时,始终返回可用于执行函数体的迭代器对象:调用迭代器的 iterator.__next__() 方法将导致函数执行,直到它使用 yield 语句。当函数执行 return 陈述或结尾脱落,a StopIteration 引发异常,迭代器将到达要返回的值集的结尾。

协程函数

一种函数或方法,用 async def 被称为 coroutine function . 这样的函数在调用时返回 coroutine 对象。它可能包含 await 表达式,以及 async withasync for 声明。也见 协程对象 部分。

异步生成器功能

一种函数或方法,用 async def 它使用 yield 语句称为 asynchronous generator function . 当调用此类函数时,返回一个异步迭代器对象,该对象可用于 async for 执行函数体的语句。

调用异步迭代器 aiterator.__anext__() 方法将返回 awaitable 它将在等待时执行,直到它使用 yield 表达式。当函数执行空 return 陈述或结尾脱落,a StopAsyncIteration 引发异常,异步迭代器将到达要生成的值集的结尾。

内置功能

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

内置方法

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

Classes

类是可调用的。这些对象通常充当它们自己的新实例的工厂,但是对于重写的类类型,可以进行更改。 __new__() .调用的参数传递给 __new__() 在典型情况下, __init__() 初始化新实例。

类实例

通过定义 __call__() 方法。

模块

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

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

预定义(可写)属性: __name__ 是模块的名称; __doc__ 是模块的文档字符串,或者 None 如果不可用; __annotations__ (可选)是包含 variable annotations 模块体执行时采集; __file__ 是从文件加载模块的文件的路径名(如果从文件加载模块)。这个 __file__ 某些类型的模块(如静态链接到解释器的C模块)可能缺少属性;对于从共享库动态加载的扩展模块,它是共享库文件的路径名。

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

CPython implementation detail: 由于cpython清除模块字典的方式,当模块超出范围时,模块字典将被清除,即使字典仍然有活动引用。要避免这种情况,请在直接使用字典时复制字典或保留模块。

自定义类

自定义类类型通常由类定义创建(请参见第节 类定义 )类具有由Dictionary对象实现的命名空间。类属性引用被转换为此字典中的查找,例如, C.x 转换为 C.__dict__["x"] (尽管有许多钩子允许使用其他方法定位属性)。如果在那里找不到属性名,则属性搜索将在基类中继续。对基类的搜索使用c3方法解析顺序,该顺序在存在“菱形”继承结构的情况下(其中有多个继承路径通向一个共同的祖先)也能正常工作。关于python使用的c3 mro的更多详细信息,可以在2.3版本附带的文档中找到,网址为https://www.python.org/download/releases/2.3/mro/。

当类属性引用(对于类 C ,例如)将生成一个类方法对象,将其转换为一个实例方法对象,该实例方法对象的 __self__ 属性是 C . 当它将生成静态方法对象时,它将被转换为由静态方法对象封装的对象。见节 实现描述符 另一种方法是,从类中检索到的属性可能与实际包含在 __dict__ .

类属性分配更新类的字典,而不是基类的字典。

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

特殊属性: __name__ 是类名; __module__ 是定义类的模块名; __dict__ 是包含类的命名空间的字典; __bases__ 是包含基类的元组,按照它们在基类列表中出现的顺序排列; __doc__ 是类的文档字符串,或者 None 如果未定义; __annotations__ (可选)是包含 variable annotations 在类体执行期间收集。

类实例

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

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

如果类实例具有具有某些特殊名称的方法,那么它们可以假装为数字、序列或映射。见节 特殊方法名称 .

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

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

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

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

内部类型

解释器内部使用的一些类型向用户公开。它们的定义可能会随着解释器的未来版本而改变,但为了完整起见,这里会提到它们。

代码对象

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

特殊只读属性: co_name 给出函数名; co_argcount 是位置参数的总数(包括仅位置参数和具有默认值的参数); co_posonlyargcount 是仅位置参数的数目(包括具有默认值的参数); co_kwonlyargcount 是仅关键字参数的数目(包括具有默认值的参数); 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 钻头 0x04 如果函数使用 *arguments 语法接受任意数量的位置参数;位 0x08 如果函数使用 **keywords 接受任意关键字参数的语法;位 0x20 如果函数是生成器,则设置。

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

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

如果代码对象表示函数,则 co_consts 是函数的文档字符串,或者 None 如果未定义。

框架对象

帧对象表示执行帧。它们可能出现在回溯对象中(见下文),也会传递给已注册的跟踪函数。

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

特殊可写属性: f_trace ,如果不是 None ,是在代码执行期间为各种事件调用的函数(这由调试器使用)。通常情况下,每个新震源测线都会触发一个事件-可通过设置禁用此功能。 f_trace_linesFalse .

启动位置 may 允许通过设置请求每个操作码事件 f_trace_opcodesTrue . 注意,如果跟踪函数引发的异常转义到被跟踪的函数,这可能导致未定义的解释程序行为。

f_lineno 是帧的当前行号---从跟踪函数中写入该行号将跳转到给定行(仅适用于最底端的帧)。调试程序可以通过写入f_lineno来实现跳转命令(也称为set next语句)。

框架对象支持一种方法:

frame.clear()

此方法清除对由帧保存的局部变量的所有引用。此外,如果框架属于一个生成器,则生成器是最终确定的。这有助于打破涉及帧对象的引用循环(例如,在捕获异常并存储其跟踪以供以后使用时)。

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

3.4 新版功能.

回溯对象

回溯对象表示异常的堆栈跟踪。当发生异常时,将隐式创建回溯对象,也可以通过调用 types.TracebackType .

对于隐式创建的回溯,当搜索异常处理程序释放执行堆栈时,在每个未释放级别上,在当前回溯前面插入一个回溯对象。当输入异常处理程序时,堆栈跟踪对程序可用。(见章节) 这个 try 陈述 )作为返回的元组的第三项可访问。 sys.exc_info() 和作为 __traceback__ 捕获的异常的属性。

当程序不包含合适的处理程序时,堆栈跟踪将被写入(格式良好的)标准错误流;如果解释器是交互式的,那么它也可以作为 sys.last_traceback .

对于显式创建的跟踪,由跟踪的创建者决定 tb_next 属性应链接以形成完整的堆栈跟踪。

特殊只读属性: tb_frame 指向当前级别的执行框架; tb_lineno 给出异常发生的行号; tb_lasti 指示精确指令。如果异常发生在 try 语句没有匹配的except子句或finally子句。

特殊可写属性: tb_next 是堆栈跟踪中的下一个级别(朝向发生异常的帧),或者 None 如果没有下一级。

在 3.7 版更改: 现在可以从python代码显式地实例化回溯对象,并且 tb_next 可以更新现有实例的属性。

切片对象

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

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

切片对象支持一种方法:

slice.indices(self, length)

此方法采用单个整数参数 长度 并计算有关切片对象将描述的切片的信息(如果应用于 长度 项目。它返回三个整数的元组;分别是 开始stop 指数与 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

当实现一个模拟任何内置类型的类时,重要的是,模拟的实现程度只能达到对被建模对象有意义的程度。例如,一些序列可以很好地检索单个元素,但提取切片可能没有意义。(其中一个例子是 NodeList W3C文档对象模型中的接口。)

3.3.1. 基本定制

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

调用以创建类的新实例 cls . __new__() 是一个静态方法(特殊情况下,因此不需要将其声明为特殊情况),它将请求实例的类作为其第一个参数。其余参数是传递给对象构造函数表达式(对类的调用)的参数。的返回值 __new__() 应该是新的对象实例(通常是 cls

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

如果 __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__() 要自定义它),不能返回非``none``值 __init__() ;这样做会导致 TypeError 在运行时引发。

object.__del__(self)

当实例即将被销毁时调用。这也称为终结器或(不正确地)析构函数。如果基类具有 __del__() 方法,派生类的 __del__() 方法(如果有)必须显式调用它,以确保正确删除实例的基类部分。

这是可能的(尽管不推荐!)对于 __del__() 方法通过创建对实例的新引用来推迟对该实例的销毁。这称为对象 复活 . 它取决于实现是否 __del__() 当一个复活的物体即将被摧毁时,第二次被称为 CPython 实现只调用一次。

不能保证 __del__() 对解释器退出时仍存在的对象调用方法。

注解

del x 不直接调用 x.__del__() ——前一种方法减少了 x 一个,后者只有在 x 的引用计数为零。

CPython implementation detail: It is possible for a reference cycle to prevent the reference count of an object from going to zero. In this case, the cycle will be later detected and deleted by the cyclic garbage collector. A common cause of reference cycles is when an exception has been caught in a local variable. The frame's locals then reference the exception, which references its own traceback, which references the locals of all frames caught in the traceback.

参见

文件 gc 模块。

警告

由于不稳定的环境 __del__() 方法将被调用,在执行过程中发生的异常将被忽略,并将警告打印到 sys.stderr 相反。特别地:

  • __del__() 可以在执行任意代码时调用,包括从任意线程调用。如果 __del__() 需要获取一个锁或调用任何其他阻塞资源,它可能会死锁,因为资源可能已被中断以执行的代码占用。 __del__() .

  • __del__() 可以在解释器关闭期间执行。因此,它需要访问的全局变量(包括其他模块)可能已被删除或设置为 None . python保证在删除其他全局变量之前从模块中删除以单个下划线开头的全局变量;如果不存在对此类全局变量的其他引用,这有助于确保导入的模块在 __del__() 方法被调用。

object.__repr__(self)

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

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

object.__str__(self)

被称为 str(object) 以及内置功能 format()print() 计算对象的“非正式”或良好打印的字符串表示。返回值必须是 string 对象。

这种方法不同于 object.__repr__() 在这一点上没有期望 __str__() 返回有效的python表达式:可以使用更方便或更简洁的表示。

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

object.__bytes__(self)

被称为 bytes 计算对象的字节字符串表示。这应该返回 bytes 对象。

object.__format__(self, format_spec)

被调用 format() 内置功能,通过扩展,评估 formatted string literals 以及 str.format() 方法生成对象的“格式化”字符串表示形式。这个 format_spec 参数是包含所需格式选项说明的字符串。解释 format_spec 参数取决于类型实现 __format__() 但是,大多数类要么将格式委托给一个内置类型,要么使用类似的格式选项语法。

格式规范小型语言 有关标准格式语法的说明。

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

在 3.4 版更改: 这个 __format__ 方法 object 本身引发了 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 机具 __eq__() 通过使用 is ,返回 NotImplemented 在错误比较的情况下: True if x is y else NotImplemented 。为 __ne__() ,默认情况下,它将委托给 __eq__() 并反转结果,除非是 NotImplemented 。比较运算符或默认实现之间没有其他隐含关系;例如, (x<y or x==y) 并不暗示 x<=y 。若要从单个根操作自动生成排序操作,请参见 functools.total_ordering()

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

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

object.__hash__(self)

由内置函数调用 hash() 以及对hash集合成员的操作,包括 setfrozensetdict . __hash__() 应返回一个整数。唯一需要的属性是,比较相等的对象具有相同的hash值;建议将对象组件的hash值混合在一起,这些组件也通过将对象打包成一个元组并对该元组进行hash处理来参与对象比较。例子::

def __hash__(self):
    return hash((self.name, self.nick, self.color))

注解

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

如果类没有定义 __eq__() 方法不应定义 __hash__() 操作;如果它定义 __eq__() 但不是 __hash__() ,其实例将不能用作hash集合中的项。如果类定义可变对象并实现 __eq__() 方法,不应实现 __hash__() ,因为可hash集合的实现要求键的hash值是不可变的(如果对象的hash值更改,它将位于错误的hash桶中)。

用户定义的类具有 __eq__()__hash__() 方法;使用它们,所有对象都比较不相等(除了它们本身)和 x.__hash__() 返回适当的值,以便 x == y 这两者都意味着 x is yhash(x) == hash(y) .

重写的类 __eq__() 并且没有定义 __hash__() 会有它的 __hash__() 隐式设置为 None . 当 __hash__() 类的方法是 None ,类的实例将引发 TypeError 当程序尝试检索其hash值时,也将在检查时正确标识为不可显示 isinstance(obj, collections.abc.Hashable) .

如果一个类重写 __eq__() 需要保持执行 __hash__() 在父类中,必须通过设置 __hash__ = <ParentClass>.__hash__ .

如果类不重写 __eq__() 希望取消hash支持,它应该包括 __hash__ = None 在类定义中。定义自己的类 __hash__() 明确提出了 TypeError 将被错误地标识为可hash的 isinstance(obj, collections.abc.Hashable) 调用。

注解

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

这是为了防止由于精心选择的输入而导致的拒绝服务,这些输入利用了dict插入的最坏情况性能,o(n^2)复杂性。有关详细信息,请参阅http://www.ocert.org/advisories/ocert-2011-003.html。

更改hash值会影响集合的迭代顺序。python从未保证过这种顺序(它通常在32位和64位构建之间变化)。

也见 PYTHONHASHSEED .

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

object.__bool__(self)

调用以实现真值测试和内置操作 bool() 应该返回 FalseTrue . 如果未定义此方法, __len__() 如果已定义,则调用,如果结果非零,则认为对象为真。如果类既没有定义 __len__() 也不 __bool__() ,它的所有实例都被认为是真的。

3.3.2. 定制属性访问

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

object.__getattr__(self, name)

当默认属性访问失败时调用 AttributeError (或 __getattribute__() 提出一个 AttributeError 因为 name 不是的实例属性或类树中的属性 self 或;或 __get__() A的 name 物业引发 AttributeError )此方法应返回(计算的)属性值或引发 AttributeError 例外。

注意,如果属性是通过正常机制找到的, __getattr__() 未调用。(这是一种故意的不对称 __getattr__()__setattr__() .)这是出于效率的考虑,否则 __getattr__() 无法访问实例的其他属性。注意,至少在实例变量中,您可以通过不在实例属性字典中插入任何值(而是将它们插入另一个对象)来伪造总控制。见 __getattribute__() 方法获取对属性访问的实际完全控制。

object.__getattribute__(self, name)

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

注解

在通过语言语法或内置函数隐式调用查找特殊方法时,仍然可以绕过此方法。见 特殊方法查找 .

引发一个 auditing event object.__getattr__ 带着参数 objname

object.__setattr__(self, name, value)

在尝试属性分配时调用。调用它而不是普通机制(即将值存储在实例字典中)。 name 是属性名, value 是要分配给它的值。

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

引发一个 auditing event object.__setattr__ 带着参数 objnamevalue

object.__delattr__(self, name)

类似于 __setattr__() 但属性删除而不是赋值。只有当 del obj.name 对对象有意义。

引发一个 auditing event 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 . 例如::

import sys
from types import ModuleType

class VerboseModule(ModuleType):
    def __repr__(self):
        return f'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__ 模块属性现在可写。

3.7 新版功能: __getattr____dir__ 模块属性。

参见

PEP 562 -模块 __getattr__ 和第二代

描述了 __getattr____dir__ 模块上的功能。

3.3.2.2. 实现描述符

以下方法仅在包含该方法的类的实例(所谓 描述符 类)出现在 owner 类(描述符必须位于所有者的类字典中,或者位于其父类的类字典中)。在下面的示例中,“属性”是指其名称是所有者类中属性的键的属性。 __dict__ .

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

调用以获取所有者类(类属性访问)或该类的实例(实例属性访问)的属性。可选的 主人 参数是所有者类,而 实例 属性是通过其访问的实例,还是 None 当通过 主人 .

此方法应返回计算的属性值或引发 AttributeError 例外。

PEP 252 指定 __get__() 可以用一个或两个参数调用。Python自己的内置描述符支持此规范;但是,某些第三方工具可能具有需要这两个参数的描述符。 Python 自己的 __getattribute__() 无论是否需要,实现总是传入这两个参数。

object.__set__(self, instance, value)

调用以设置实例的属性 实例 将所有者类转换为新值, value .

注释,添加 __set__()__delete__() 将描述符的类型更改为“数据描述符”。见 调用描述符 了解更多详细信息。

object.__delete__(self, instance)

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

object.__set_name__(self, owner, name)

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

注解

__set_name__() 仅作为 type 构造函数,因此在初始创建后将描述符添加到类时,需要使用适当的参数显式调用它:

class A:
   pass
descr = custom_descriptor()
A.attr = descr
descr.__set_name__(A, 'attr')

创建类对象 了解更多详细信息。

3.6 新版功能.

属性 __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) .

超级装订

如果 a 是的实例 super ,然后是绑定 super(B, obj).m() 搜索 obj.__class__.__mro__ 对于基类 A 在前面 B 然后用调用调用描述符: A.__dict__['m'].__get__(obj, obj.__class__) .

例如绑定,描述符调用的优先级取决于定义的描述符方法。描述符可以定义 __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__

这个类变量可以被分配一个字符串、iterable或字符串序列,这些字符串的变量名由实例使用。 __slots__ 为声明的变量保留空间,并阻止自动创建 __dict____weakref__ 对于每个实例。

3.3.2.4.1. 使用注意事项 __slots__
  • 从类继承时 __slots__ , the __dict____weakref__ 实例的属性将始终可访问。

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

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

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

  • A的作用 __slots__ 声明不限于定义它的类。 __slots__ 在父类中声明的在子类中可用。但是,子类将得到 __dict____weakref__ 除非他们也定义 __slots__ (只应包含 额外的 插槽)。

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

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

  • 任何非字符串iterable都可以分配给 __slots__ . 也可以使用映射;但是,在将来,可以为每个键对应的值赋予特殊含义。

  • __class__ 只有当两个类具有相同的属性时,赋值才有效 __slots__ .

  • 可以使用具有多个时隙父类的多个继承,但只允许一个父类具有由时隙创建的属性(其他基必须具有空的时隙布局)-冲突引发 TypeError .

  • 如果迭代器用于 __slots__ 然后为迭代器的每个值创建一个描述符。然而 __slots__ 属性将是空迭代器。

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

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

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

  • 解决MRO条目;

  • 确定适当的元类;

  • 准备类名称空间;

  • 执行该类主体;

  • 类对象被创建。

3.3.3.2. 解析MRO条目

如果类定义中出现的基不是 type 然后 __mro_entries__ 方法被搜索到。如果找到,则使用原始的基元组调用它。此方法必须返回将使用的类的元组,而不是此基。元组可以为空,在这种情况下,将忽略原始基。

参见

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

3.3.3.3. 确定适当的元类

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

  • 如果没有给出基和显式元类,那么 type() 使用;

  • 如果给定了显式元类,并且它是 not 一个实例 type() ,则直接用作元类;

  • 如果 type() 作为显式元类给出,或定义基,然后使用最派生的元类。

最派生的元类是从显式指定的元类(如果有)和元类(即 type(cls) )所有指定的基类。最派生的元类是 all 这些候选元类。如果所有候选元类都不满足该条件,那么类定义将失败 TypeError .

3.3.3.4. 正在准备类命名空间

一旦识别出适当的元类,就可以准备类名称空间。如果元类具有 __prepare__ 属性,它被称为 namespace = metaclass.__prepare__(name, bases, **kwds) (如果有额外的关键字参数,则来自类定义)。这个 __prepare__ 方法应实现为 classmethod() . 返回的命名空间 __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__

类对象是将由零参数形式引用的对象。 super() . __class__ 如果类体中的任何方法引用 __class__super . 这允许零参数形式 super() 为了正确地标识基于词法作用域定义的类,而用于进行当前调用的类或实例是基于传递给方法的第一个参数来标识的。

CPython implementation detail: 在CPython 3.6及更高版本中, __class__ 单元格作为 __classcell__ 类命名空间中的项。如果存在,则必须将其传播到 type.__new__ 调用以正确初始化类。不这样做将导致 RuntimeError 在Python 3.8中。

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

  • 第一, type.__new__ 收集类命名空间中定义 __set_name__() 方法;

  • 第二,所有这些 __set_name__ 调用方法时要定义类和指定的特定描述符的名称;

  • 最后, __init_subclass__() 在新类的直接父类上按方法解析顺序调用hook。

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

当新类由创建时 type.__new__ ,将作为命名空间参数提供的对象复制到新的有序映射,并放弃原始对象。新副本封装在只读代理中,该代理成为 __dict__ 类对象的属性。

参见

PEP 3135 新超级

描述隐式 __class__ 闭合参考

3.3.3.7. 用于元类

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

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

以下方法用于重写 isinstance()issubclass() 内置功能。

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

class.__instancecheck__(self, instance)

返回true if 实例 应被视为 classes . 如果已定义,则调用以实现 isinstance(instance, class) .

class.__subclasscheck__(self, subclass)

返回true if 子类 应被视为 classes . 如果已定义,则调用以实现 issubclass(subclass, class) .

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

参见

PEP 3119 -介绍抽象基类

包括自定义规范 isinstance()issubclass() 行为通过 __instancecheck__()__subclasscheck__() ,以及在添加抽象基类的上下文中使用此功能的动机(请参见 abc 模块)到语言。

3.3.5. 模拟泛型类型

可以实现由指定的泛型类语法 PEP 484 (例如 List[int] )通过定义特殊方法:

classmethod object.__class_getitem__(cls, key)

返回一个对象,该对象表示在 key .

这个方法是在类对象本身上查找的,当在类体中定义时,这个方法隐式地是一个类方法。注意,此机制主要保留用于静态类型提示,不鼓励其他用法。

参见

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

3.3.6. 模拟可调用对象

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

当实例作为函数“调用”时调用;如果定义了此方法, x(arg1, arg2, ...) 大致翻译为 type(x).__call__(x, arg1, ...)

3.3.7. 模拟包容器类型

可以定义以下方法来实现容器对象。容器通常是序列(如列表或元组)或映射(如字典),但也可以表示其他容器。第一组方法用于模拟序列或模拟映射;区别在于对于序列,允许的键应该是整数。 k 为此 0 <= k < N 在哪里? N 序列或切片对象的长度,它定义一个项目范围。还建议映射提供方法 keys()values()items()get()clear()setdefault()pop()popitem()copy()update() 行为类似于Python的标准字典对象。这个 collections.abc 模块提供 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__() 应该遍历对象的键;对于序列,应该遍历值。

object.__len__(self)

调用以实现内置函数 len() . 应返回对象的长度,一个整数 >= 0。另外,一个没有定义 __bool__() 方法和谁的 __len__() 方法返回零在布尔上下文中被视为假。

CPython implementation detail: 在CPython中,长度要求最多为 sys.maxsize . 如果长度大于 sys.maxsize 一些功能(例如 len() 可以提高 OverflowError . 防止升高 OverflowError 通过真值测试,对象必须定义 __bool__() 方法。

object.__length_hint__(self)

调用以实现 operator.length_hint() . 应返回对象的估计长度(可能大于或小于实际长度)。长度必须为整数 >= 0个。返回值也可以是 NotImplemented ,其处理方式与 __length_hint__ 方法根本不存在。这种方法纯粹是一种优化,不需要正确性。

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.__setitem__(self, key, value)

调用以实现对的赋值 self[key] . 与…相同的注释 __getitem__() . 如果对象支持更改键的值,或者可以添加新键,或者如果可以替换元素,则只应为映射实现此功能。同样的例外情况也应该针对不适当的 key 价值观 __getitem__() 方法。

object.__delitem__(self, key)

调用以实现删除 self[key] . 与…相同的注释 __getitem__() . 只有在对象支持删除键的情况下,才应为映射实现此功能;如果可以从序列中删除元素,则应为序列实现此功能。同样的例外情况也应该针对不适当的 key 价值观 __getitem__() 方法。

object.__missing__(self, key)

被称为 dict \ __getitem__() 实施 self[key] 当关键字不在字典中时,用于dict子类。

object.__iter__(self)

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

迭代器对象还需要实现这个方法;它们需要返回自己。有关迭代器对象的详细信息,请参见 迭代器类型 .

object.__reversed__(self)

reversed() 实现反向迭代的内置功能。它应该返回一个新的迭代器对象,该对象以相反的顺序遍历容器中的所有对象。

如果 __reversed__() 未提供方法, reversed() 内置将返回到使用序列协议 (__len__()__getitem__() )支持序列协议的对象应该只提供 __reversed__() 如果它们能够提供比 reversed() .

成员资格测试操作员 (innot in )通常通过容器实现为迭代。但是,容器对象可以为下面的特殊方法提供更有效的实现,这也不要求对象是可iterable的。

object.__contains__(self, item)

调用以实现成员资格测试运算符。如果 item 是在 self ,否则为false。对于映射对象,这应该考虑映射的键,而不是值或键项对。

对于未定义的对象 __contains__() ,成员资格测试首先通过 __iter__() ,然后通过 __getitem__()this section in the language reference .

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)

调用这些方法来实现二进制算术运算 (+-, *, @, /, `` /; ``%`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[, modulo])
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__() 方法, y.__rsub__(x) 称为 x.__sub__(y) 返回 NotImplemented .

注意三元 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 . 在某些情况下,增加的赋值可能导致意外错误(请参见 为什么要做一个元组 [i] += [“项目”] 添加工作时引发异常? ,但此行为实际上是数据模型的一部分。

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() . 除非 纳迪斯 传递给 __round__() 所有这些方法都应将截断对象的值返回到 Integral (通常是 int

如果 __int__() 未定义,则内置函数 int() 回落到 __trunc__() .

3.3.9. 使用语句上下文管理器

A 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.10. 特殊方法查找

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

>>> 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__() 方法。 Coroutine objects 从回来 async def 功能正在等待。

注解

这个 generator iterator 从装饰有 types.coroutine()asyncio.coroutine() 也是可以等待的,但它们没有实现 __await__() .

object.__await__(self)

必须返回 iterator .应用于实现 awaitable 物体。例如, asyncio.Future 实现此方法以与 await 表达式。

3.5 新版功能.

参见

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

3.4.2. 协程对象

Coroutine objectsawaitable 物体。协同程序的执行可以通过调用 __await__() 并对结果进行迭代。当协同程序完成执行并返回后,迭代器将引发 StopIteration 和例外情况 value 属性保存返回值。如果协同程序引发异常,则由迭代器传播。协程不应直接引发未处理的 StopIteration 例外情况。

协程也有下面列出的方法,这些方法类似于生成器的方法(参见 生成器迭代器方法 )但是,与生成器不同,协同程序不直接支持迭代。

在 3.5.2 版更改: 这是一个 RuntimeError 不止一次的等待。

coroutine.send(value)

开始或继续执行协同程序。如果 valueNone ,这相当于前进由返回的迭代器 __await__() . 如果 value 不是 None ,此方法委托给 send() 导致协程挂起的迭代器的方法。结果(返回值, StopIteration (或其他异常)与在 __await__() 返回值,如上所述。

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

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

coroutine.close()

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

协程对象将在即将被销毁时使用上述过程自动关闭。

3.4.3. 异步迭代器

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

异步迭代器可用于 async for 语句。

object.__aiter__(self)

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

object.__anext__(self)

必须返回 可期待的 产生迭代器的下一个值。应该提高 StopAsyncIteration 迭代结束时出错。

异步可Iterable对象的示例:

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.7 版更改: 在python 3.7之前, __aiter__ 可以返回 可期待的 这将决定 asynchronous iterator .

从python 3.7开始, __aiter__ 必须返回异步迭代器对象。返回任何其他内容将导致 TypeError 错误。

3.4.4. 异步上下文管理器

异步上下文管理器 是一个 上下文管理器 能够在其 __aenter____aexit__ 方法。

异步上下文管理器可用于 async with 语句。

object.__aenter__(self)

语义上类似于 __enter__() 唯一的区别是它必须返回 可期待的 .

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

语义上类似于 __exit__() 唯一的区别是它必须返回 可期待的 .

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

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__() --失败,则不支持整体操作,所以不调用反射的方法。