3.数据模型

 

3.1。对象,值和类型

对象是Python的数据抽象。Python程序中的所有数据都由对象或对象之间的关系表示。(从某种意义上说,并且与Von Neumann的“存储程序计算机”模型一致,代码也由对象表示。)

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

CPython实现细节:对于CPython,id(x)是存储的内存地址x

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

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

对象永远不会被明确销毁; 然而,当它们变得无法到达时,它们可能被垃圾收集。允许实现推迟垃圾收集或完全省略垃圾收集 – 只要没有收集到仍然可以访问的对象,实现垃圾收集的实现方式就是如此。

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

请注意,使用实现的跟踪或调试工具可以使对象保持活动状态,这些对象通常是可收集的。另请注意,使用“ try… except”语句捕获异常可能会使对象保持活动状态。

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

一些对象包含对其他对象的引用; 这些被称为容器。容器的示例是元组,列表和字典。引用是容器值的一部分。在大多数情况下,当我们谈论容器的价值时,我们暗示的是价值,而不是所包含对象的身份; 但是,当我们谈论容器的可变性时,只隐含了直接包含的对象的标识。因此,如果不可变容器(如元组)包含对可变对象的引用,则如果更改了可变对象,则其值会更改。

类型几乎影响对象行为的所有方面。甚至对象标识的重要性在某种意义上也受到影响:对于不可变类型,计算新值的操作实际上可以返回对具有相同类型和值的任何现有对象的引用,而对于可变对象,这是不允许的。例如,后,以及可能或不可能指向同一个对象,具有值之一,这取决于执行,但之后,并保证是指两个不同的,独特的,新创建的空列表。(请注意,分配相同的对象到两个 和。)a = 1; b = 1abc = []; d = []cdc = d = []cd

 

3.2。标准类型层次结构

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

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

没有

此类型具有单个值。有一个具有此值的对象。可以通过内置名称访问此对象None。它用于表示在许多情况下缺少值,例如,它从未明确返回任何内容的函数返回。它的真值是错误的。

未实现

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

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

省略

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

numbers.Number

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

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

numbers.Integral

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

有两种类型的整数:

整数(int

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

这些代表真值False和True。代表值的两个对象False,并True是唯一的Boolean对象。布尔类型是整数类型的子类型,并且布尔值在几乎所有上下文中的行为分别类似于值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的所有项目,使得<= < j。当用作表达式时,切片是相同类型的序列。这意味着索引集重新编号,以便从0开始。

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

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

不可变的序列

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

以下类型是不可变序列:

字符串

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

元组

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

字节

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

可变序列

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

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

清单

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

字节数组

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

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

设置类型

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

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

目前有两种固有的集合类型:

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

冷冻套装

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

映射

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

目前有一种内在映射类型:

字典

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

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

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

可调用的类型

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

用户定义的函数

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

特殊属性:

属性 含义
__doc__ 函数的文档字符串,或者None如果不可用; 不是由子类继承的 可写
__name__ 功能的名称 可写
__qualname__

函数的 限定名称

版本3.3中的新功能。

可写
__module__ 定义函数的模块的名称,或者 None如果不可用。 可写
__defaults__ 包含具有默认值的参数的默认参数值的元组,或者None如果没有参数具有默认值的元组 可写
__code__ 代码对象表示已编译的函数体。 可写
__globals__ 对包含函数全局变量的字典的引用 – 定义函数的模块的全局命名空间。 只读
__dict__ 支持任意函数属性的命名空间。 可写
__closure__ None或者包含函数自由变量绑定的单元格元组。有关该cell_contents 属性的信息,请参见下文。 只读
__annotations__ 包含参数注释的字典。dict的键是参数名称'return',如果提供,则为返回注释。 可写
__kwdefaults__ 包含仅关键字参数的默认值的dict。 可写

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

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

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

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

实例方法

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

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

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

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

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

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

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

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

当一个实例方法对象是从一个类的方法对象派生,存储在“类的实例” __self__实际上是类本身,使得调用或者x.f(1)C.f(1)等同于调用f(C,1),其中f是底层功能。

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

发电机功能

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

协同功能

使用定义的函数或方法称为协程函数。这样的函数在被调用时返回一个 协程对象。它可能包含表情,以及和报表。另请参见Coroutine Objects部分。async defawaitasync withasync for

异步发电机功能

使用该语句定义的函数或方法称为 异步生成器函数。这样的函数在被调用时返回一个异步迭代器对象,该对象可以在 语句中用于执行函数体。async defyieldasync for

调用异步迭代器aiterator.__anext__()方法将返回一个等待的,等待它将执行,直到它使用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语句调用 ,或调用诸如importlib.import_module()内置 函数__import__()。模块对象具有由字典对象实现的名称空间(这是由__globals__ 模块中定义的函数的属性引用的字典)。属性引用被转换为此字典中的查找,例如,m.x等同于m.__dict__["x"]。模块对象不包含用于初始化模块的代码对象(因为初始化完成后不需要它)。

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

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

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

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

自定义类

自定义类类型通常由类定义创建(请参阅类定义一节 )。类具有由字典对象实现的命名空间。类属性引用被转换为该字典中的查找,例如, 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__(可选)是包含在类主体执行期间收集的变量注释的字典。

类实例

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

属性分配和删除更新实例的字典,而不是类的字典。如果类具有__setattr__()或 __delattr__()方法,则调用此方法而不是直接更新实例字典。

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

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

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

一个文件对象代表一个打开的文件。各种快捷键可用于创建文件对象:open()内置函数,并且还os.popen()os.fdopen()和 makefile()插座对象的方法(也许通过扩展模块提供的其它功能或方法)。

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

内部类型

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

代码对象

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

特殊的只读属性: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_flags0x04如果函数使用*arguments语法接受任意数量的位置参数,则设置位; 0x08如果函数使用**keywords语法接受任意关键字参数,则设置 bit ; 0x20如果函数是生成器,则设置位。

未来的特征声明()也使用位来指示代码对象是否在启用特定功能的情况下编译:如果函数是在未来分区启用的情况下编译的,则设置位; 位和在早期版本的Python中使用。from __future__ importdivisionco_flags0x20000x100x1000

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

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

框架对象

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

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

特殊可写属性:f_trace如果不是None,则是在代码执行期间调用各种事件的函数(由调试器使用)。通常会为每个新的源代码行触发一个事件 – 可以通过设置f_trace_lines为禁用此功能False

实现可以允许通过设置f_trace_opcodes来请求每操作码事件 True。请注意,如果跟踪函数引发的异常转义为正在跟踪的函数,则可能会导致未定义的解释器行为。

f_lineno是帧的当前行号 – 从跟踪函数内写入此跳转到给定行(仅适用于最底部的帧)。调试器可以通过写入f_lineno来实现Jump命令(也称为Set Next Statement)。

Frame对象支持一种方法:

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代码显式实例化Traceback对象,并且tb_next可以更新现有实例的属性。

切片对象

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

特殊的只读属性:start是下限; stop是上限; step是步骤值; None如果省略则各自。这些属性可以是任何类型。

切片对象支持一种方法:

slice.indices自我长度
此方法采用单个整数参数长度,并计算切片对象在应用于一系列长度项时将描述的切片的信息。它返回一个由三个整数组成的元组; 这些分别是开始停止指数与 步骤切片的或步幅长度。以与常规切片一致的方式处理丢失或越界索引。
静态方法对象
静态方法对象提供了一种破坏函数对象到上述方法对象的转换的方法。静态方法对象是任何其他对象的包装器,通常是用户定义的方法对象。当从类或类实例中检索静态方法对象时,实际返回的对象是包装对象,该对象不受任何进一步转换的影响。静态方法对象本身不可调用,尽管它们通常包装的对象是。静态方法对象由内置 staticmethod()构造函数创建。
类方法对象
类方法对象(如静态方法对象)是另一个对象的包装器,它改变了从类和类实例中检索该对象的方式。上面在“用户定义的方法”下描述了类方法对象在这种检索时的行为。类方法对象由内置classmethod()构造函数创建。

 

3.3。特殊方法名称

类可以通过定义具有特殊名称的方法来实现由特殊语法(例如算术运算或下标和切片)调用的某些操作。这是Python的运算符重载方法,允许类根据语言运算符定义自己的行为。例如,如果一个类定义了一个名为的方法__getitem__(),并且x是该类的一个实例,则x[i]大致相当于。除非另有说明,否则在未定义适当方法时(通常或),尝试执行操作会引发异常 。type(x).__getitem__(x, i)AttributeErrorTypeError

设置一种特殊方法以None指示相应的操作不可用。例如,如果一个类设置 __iter__()None,则该类不可迭代,因此调用 iter()其实例将引发一个TypeError(不再回退__getitem__())。[2]

在实现模拟任何内置类型的类时,重要的是只能将仿真实现到对建模对象有意义的程度。例如,某些序列可以很好地检索单个元素,但提取切片可能没有意义。(这方面的一个例子是NodeListW3C的文档对象模型中的接口。)

 

3.3.1。基本定制

object.__new__cls [… 

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

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

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

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

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

object.__init__[… 

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

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

object.__del__自我

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

有可能(尽管不推荐!)__del__()方法通过创建对它的新引用来推迟实例的销毁。这称为物体复活。它是依赖于实现的,无论__del__()是在复活的对象即将被摧毁时第二次被调用; 当前的CPython实现只调用一次。

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

注意

del x不直接调用x.__del__()– 前者将引用计数减x1,后者仅在x引用计数达到零时调用 。

CPython实现细节:参考周期可以防止对象的引用计数变为零。在这种情况下,循环垃圾收集器稍后将检测并删除该循环。引用周期的常见原因是在局部变量中捕获到异常。然后框架的本地引用异常,该异常引用自己的回溯,该回溯引用回溯中捕获的所有帧的本地。

也可以看看

gc模块的文档。

警告

由于__del__()调用方法的不稳定环境,将忽略执行期间发生的异常,并打印警告sys.stderr。特别是:

  • __del__()可以在执行任意代码时调用,包括来自任意线程。如果__del__()需要锁定或调用任何其他阻塞资源,它可能会死锁,因为资源可能已被执行中断的代码占用__del__()
  • __del__()可以在解释器关闭期间执行。因此,它需要访问的全局变量(包括其他模块)可能已被删除或设置为None。Python保证在删除其他全局变量之前,从其模块中删除名称以单个下划线开头的全局变量; 如果不存在对此类全局变量的其他引用,这可能有助于确保在__del__()调用方法时导入的模块仍然可用 。

 

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

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

 

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

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

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

object.__bytes__自我

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

 

object.__format__selfformat_spec 
format()内置函数调用,并通过扩展,评估格式化的字符串文字str.format()方法,以生成对象的“格式化”字符串表示。的format_spec参数是包含所期望的格式选项的描述的字符串。对format_spec参数的解释取决于实现的类型__format__(),但是大多数类将格式化为一种内置类型,或者使用类似的格式化选项语法。

有关标准格式语法的说明,请参阅格式规范迷你语言

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

在版本3.4中更改: __format__方法object本身会引发一个TypeError if传递的任何非空字符串。

在版本3.7中更改:现在相当于而不是。object.__format__(x, '')str(x)format(str(self), '')

 

object.__lt__自我其他
object.__le__自我其他
object.__eq__自我其他
object.__ne__自我其他
object.__gt__自我其他
object.__ge__自我其他

这些是所谓的“丰富比较”方法。运算符符号和方法名称之间的对应关系如下: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()该值以确定结果是true还是false。

默认情况下,除非是结果,否则__ne__()委托__eq__()并反转结果NotImplemented。比较运算符之间没有其他隐含的关系,例如,事实并非暗示。要从单个根操作自动生成排序操作,请参阅。(x<y orx==y)x<=yfunctools.total_ordering()

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

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

object.__hash__自我

通过内置的函数调用hash()和杂乱的集合的成员包括运营setfrozenset和 dict。 __hash__()应该返回一个整数。唯一需要的属性是比较相等的对象具有相同的哈希值; 建议将对象组件的哈希值混合在一起,这些哈希值也通过将对象打包成元组并对元组进行散列来对比对象。例:

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

注意

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

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

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

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

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

如果一个不重写的类__eq__()希望禁止哈希支持,它应该包含在类定义中。定义自己明确引发a的类将被错误地识别为可通过调用进行清除。__hash__ = None__hash__()TypeErrorisinstance(obj,collections.abc.Hashable)

注意

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

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

更改哈希值会影响集的迭代顺序。Python从未对此排序做出保证(通常在32位和64位版本之间有所不同)。

也可以看看 PYTHONHASHSEED

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

object.__bool__自我

被称为实施真值测试和内置操作 bool(); 应该返回FalseTrue。如果未定义此方法,__len__()则调用此方法(如果已定义),如果其结果为非零,则将该对象视为true。如果一个类既未定义也 __len__()未定义__bool__(),则其所有实例都被视为真。

 

3.3.2。自定义属性访问

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

object.__getattr__自我名字
当默认的属性访问失败,并调用AttributeError (或者__getattribute__()引发AttributeError,因为 名字是不是一个实例的属性或分类的属性self;或__get__()名称属性提升 AttributeError)。此方法应返回(计算)属性值或引发AttributeError异常。

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

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

注意

当通过语言语法或内置函数进行隐式调用查找特殊方法时,仍然可以绕过此方法。请参阅特殊方法查找

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

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

object.__delattr__自我名字
喜欢__setattr__()但是对于属性删除而不是赋值。只有对对象有意义时才应该实现。del obj.name
object.__dir__自我
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__和__dir__
描述模块的功能__getattr____dir__功能。

 

3.3.2.2。实现描述符

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

object.__get__自我实例所有者
调用以获取所有者类(类属性访问)或该类的实例(实例属性访问)的属性。owner始终是所有者类,而instanceNone通过所有者访问属性时访问该属性的实例。此方法应返回(计算)属性值或引发AttributeError 异常。
object.__set__自我实例价值
调用以将所有者类的实例实例上的属性设置为新值value
object.__delete__自我实例
被调用以删除所有者类的实例实例上的属性。
object.__set_name__自己所有者名字
在创建拥有类所有者时调用。描述符已分配给name

版本3.6中的新功能。

该属性__objclass__inspect模块解释为指定定义此对象的类(适当地设置它可以帮助运行时内省动态类属性)。对于callables,它可能表示期望类型(或子类)的实例是第一个位置参数(例如,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__ABA.__dict__['m'].__get__(obj, obj.__class__)

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

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

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

 

3.3.2.4。__slots__

__slots__允许我们显式声明数据成员(如属性)并拒绝创建__dict____weakref__ (除非在__slots__中明确声明或在父级中可用。)

使用__dict__节省的空间可能很重要。

object.__slots__
可以为此类变量分配字符串,可迭代或具有实例使用的变量名称的字符串序列。 __slots__为声明的变量保留空间,并阻止为每个实例自动创建__dict__ 和__weakref__
3.3.2.4.1。关于使用__slots__的注意事项
  • 从没有__slots__的类继承时,实例的__dict__和 __weakref__属性将始终可访问。
  • 如果没有__dict__变量,则无法为实例分配__slots__定义中未列出的新变量。尝试分配到不公开的变量名称会引发AttributeError。如果需要动态分配新变量,则'__dict__'__slots__声明中添加字符串序列。
  • 如果没有每个实例的__weakref__变量,则定义__slots__的类 不支持对其实例的弱引用。如果需要弱引用支持,则'__weakref__'__slots__声明中添加字符串序列 。
  • 通过为每个变量名创建描述符(实现描述符),在类级别实现__slots__。因此,类属性不能用于为__slots__定义的实例变量设置默认值 ; 否则,class属性将覆盖描述符赋值。
  • 一个的动作__slots__声明不局限于定义它的类。 在父母中声明的__slots__可用于子课程。然而,孩子的子类将得到一个__dict__ 和 __weakref__,除非他们还定义__slots__(它应该只包含任何的名称额外插槽)。
  • 如果类定义了也在基类中定义的槽,则基类槽定义的实例变量是不可访问的(除非直接从基类检索其描述符)。这使得程序的含义未定义。将来,可能会添加一项检查以防止这种情况发生。
  • 非空__slots__不适用于从“可变长度”内置类型派生的类,例如intbytestuple
  • 可以将任何非字符串可迭代分配给__slots__。也可以使用映射; 但是,将来可以为与每个键对应的值分配特殊含义。
  • __class__赋值仅在两个类具有相同的__slots__时有效
  • 可以使用具有多个时隙父类的多重继承,但是只允许一个父级具有由插槽创建的属性(其他基础必须具有空插槽布局) – 违规引发 TypeError

 

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()使用
  • 如果给出了显式元类并且它不是实例 type(),那么它将直接用作元类
  • 如果type()给出了一个实例作为显式元类,或者定义了基数,则使用最派生的元类

最派生的元类是从显式指定的元类(如果有)和type(cls)所有指定基类的元类(即)中选择的。最衍生的元类是所有 这些候选元类的子类型。如果候选元类都不符合该标准,那么类定义将失败TypeError

 

3.3.3.4。准备类命名空间

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

如果元类没有__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实现细节:在CPython 3.6及更高版本中,__class__单元作为__classcell__类命名空间中的条目传递给元类。如果存在,则必须将其传播到type.__new__调用,以便正确初始化类。如果不这样做,将导致DeprecationWarningPython 3.6和RuntimeErrorPython 3.8。

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

  • 首先,type.__new__收集定义__set_name__()方法的类命名空间中的所有描述符;
  • 第二,所有这些__set_name__方法都是在定义类和特定描述符的指定名称的情况下调用的; 和
  • 最后,__init_subclass__()在方法解析顺序中,在新类的直接父级上调用该钩子。

创建类对象后,将其传递给类定义中包含的类装饰器(如果有),并将结果对象作为定义的类绑定在本地名称空间中。

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

也可以看看

PEP 3135 – 新超级
描述隐式__class__闭包引用

3.3.3.7。用于元类的

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

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

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

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

class.__instancecheck__自我实例
返回true如果实例应该考虑的(直接或间接)的实例。如果定义,则调用实现。isinstance(instance,class)
class.__subclasscheck__自我子类
返回true,如果子类应考虑的(直接或间接)子。如果定义,则调用实现。issubclass(subclass, class)

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

也可以看看

PEP 3119 – 介绍抽象基类
包括定制规范isinstance(),并 issubclass()通过行为__instancecheck__()和 __subclasscheck__()与动机中添加抽象基类的背景下,这个功能(见abc 模块)的语言。

3.3.5。模拟泛型类型

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

classmethod object.__class_getitem__clskey 
通过key中的类型参数返回表示泛型类特化的对象。

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

也可以看看

PEP 560 – 对键入模块和泛型类型的核心支持

 

3.3.6。模拟可调用对象

object.__call__self [args … 

当实例被“调用”为函数时调用; 如果定义了这个方法, 则是一个简写。x(arg1, arg2, ...)x.__call__(arg1,arg2, ...)

 

3.3.7。模拟容器类型

可以定义以下方法来实现容器对象。容器通常是序列(例如列表或元组)或映射(如字典),但也可以表示其他容器。第一组方法用于模拟序列或模拟映射; 不同之处在于,对于序列,允许键应该是整数k,其中N是序列的长度,或切片对象,它们定义了一系列项。它也建议映射提供方法 ,,,,, ,,,,和 行为类似于对Python的标准字典对象。该0 <= k <Nkeys()values()items()get()clear()setdefault()pop()popitem()copy()update()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__()应该是一样的 keys(); 对于序列,它应该迭代值。

object.__len__自我

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

CPython实现细节:在CPython中,长度最多需要sys.maxsize。如果长度大于sys.maxsize某些特征(例如len())可能会提高OverflowError。为了防止OverflowError通过真值测试提高 ,对象必须定义一个__bool__()方法。

object.__length_hint__自我
被叫实施operator.length_hint()。应返回对象的估计长度(可能大于或小于实际长度)。长度必须是整数>=0.此方法纯粹是一种优化,并且永远不需要正确性。

版本3.4中的新功能。

注意

切片仅使用以下三种方法完成。像这样的电话

a[1:2] = b

 

被翻译成

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

 

等等。始终填写缺少切片项目None

object.__getitem__自我钥匙
被要求实施评估self[key]。对于序列类型,接受的键应该是整数和切片对象。请注意,负索引的特殊解释(如果类希望模拟序列类型)取决于__getitem__()方法。如果密钥类型不合适,TypeError可能会引发; 如果某个值超出了序列的索引集(在负值的任何特殊解释之后),则 IndexError应该引发。对于映射类型,如果缺少密钥(不在容器中),则KeyError应该引发。

注意

for循环期望IndexError为非法索引引发一个允许正确检测序列结束的循环。

object.__setitem__自我钥匙价值
被要求实施任务self[key]。同样的注意事项 __getitem__()。如果对象支持对键值的更改,或者是否可以添加新键,或者对于可以替换元素的序列,则只应对映射实现此操作。同样的例外情况应提高不当作为值__getitem__()的方法。
object.__delitem__自我钥匙
被称为实施删除self[key]。同样的注意事项 __getitem__()。如果对象支持删除键,则应仅对映射实现此操作,或者如果可以从序列中删除元素,则仅对序列实现此操作。同样的例外情况应提高不当 作为值__getitem__()的方法。
object.__missing__自我钥匙
叫做dict。当key不在字典中时__getitem__()实现self[key]dict子类。
object.__iter__自我
当容器需要迭代器时,将调用此方法。此方法应返回一个新的迭代器对象,该对象可以迭代容器中的所有对象。对于映射,它应该迭代容器的键。

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

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

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

成员资格测试运算符(in和)通常实现为序列的迭代。但是,容器对象可以使用更高效的实现提供以下特殊方法,这也不要求对象是序列。not in

object.__contains__自我项目
被称为实施会员测试运营商。如果item 在self中,则返回true ,否则返回false。对于映射对象,这应该考虑映射的键而不是值或键 – 项对。

对于未定义的对象,__contains__()成员资格测试首先尝试迭代__iter__(),然后是旧的序列迭代协议__getitem__(),请参阅语言参考中的此部分

 

3.3.8。模拟数字类型

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

object.__add__自我其他
object.__sub__自我其他
object.__mul__自我其他
object.__matmul__自我其他
object.__truediv__自我其他
object.__floordiv__自我其他
object.__mod__自我其他
object.__divmod__自我其他
object.__pow__selfother [modulo 
object.__lshift__自我其他
object.__rshift__自我其他
object.__and__自我其他
object.__xor__自我其他
object.__or__自我其他

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

如果其中一个方法不支持使用提供的参数进行操作,则应返回NotImplemented

object.__radd__自我其他
object.__rsub__自我其他
object.__rmul__自我其他
object.__rmatmul__自我其他
object.__rtruediv__自我其他
object.__rfloordiv__自我其他
object.__rmod__自我其他
object.__rdivmod__自我其他
object.__rpow__自我其他
object.__rlshift__自我其他
object.__rrshift__自我其他
object.__rand__自我其他
object.__rxor__自我其他
object.__ror__自我其他

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

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

注意

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

object.__iadd__自我其他
object.__isub__自我其他
object.__imul__自我其他
object.__imatmul__自我其他
object.__itruediv__自我其他
object.__ifloordiv__自我其他
object.__imod__自我其他
object.__ipow__selfother [modulo 
object.__ilshift__自我其他
object.__irshift__自我其他
object.__iand__自我其他
object.__ixor__自我其他
object.__ior__自我其他
这些方法称为实现增强的算术作业(+=-=*=@=/=//=%=**=<<=, >>=&=^=|=)。这些方法应该尝试就地执行操作(修改self)并返回结果(可能是,但不一定是self)。如果未定义特定方法,则扩充分配将回退到常规方法。例如,如果x 是带有__iadd__()方法的类的实例,则等效于。否则,并 考虑与评估一样x += yx = x.__iadd__(y)x.__add__(y)y.__radd__(x)x + y。在某些情况下,增强赋值可能会导致意外错误(请参阅 为什么a_tuple [i] + = [‘item’]在添加时会引发异常?),但这种行为实际上是数据模型的一部分。
object.__neg__自我
object.__pos__自我
object.__abs__自我
object.__invert__自我

调用来实现一元算术运算(-+abs() 和~)。

object.__complex__自我
object.__int__自我
object.__float__自我

被称为实现内置函数complex(), int()float()。应该返回适​​当类型的值。

object.__index__自我
称为实施operator.index(),并且无论何时需要的Python于数字对象无损转换为整数对象(如在切片,或在内置的bin()hex()oct() 函数)。此方法的存在表明数字对象是整数类型。必须返回一个整数。

注意

为了得到一个连贯的整数类型类,__index__()定义的时候也__int__()应该被定义,并且两者都应该返回相同的值。

object.__round__[ndigits 
object.__trunc__自我
object.__floor__自我
object.__ceil__自我

被称为实现内置功能round()math 功能trunc()floor()以及ceil()。除非将ndigits传递给__round__()所有这些方法,否则应将截断的对象的值返回到Integral (通常为a int)。

如果__int__()未定义,则内置函数将int() 回退__trunc__()

 

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

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

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

有关上下文管理器的更多信息,请参阅Context Manager Types

object.__enter__自我
输入与此对象相关的运行时上下文。该with语句将此方法的返回值绑定到as语句子句中指定的目标( 如果有)。
object.__exit__selfexc_typeexc_valuetraceback 
退出与此对象相关的运行时上下文。参数描述导致退出上下文的异常。如果上下文没有异常退出,那么所有三个参数都将是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__()以这种方式绕过机器为解释器内的速度优化提供了很大的空间,代价是处理特殊方法的一些灵活性(必须在类对象本身上设置特殊方法以便由解释器一致地调用) 。

 

3.4。协同程序

3.4.1。等待对象

一个awaitable对象一般实现的__await__()方法。 从函数返回的协程对象是等待的。async def

注意

生成器返回的生成器迭代器对象装饰着types.coroutine()或者asyncio.coroutine() 也是等待的,但是它们没有实现__await__()

object.__await__自我
必须返回迭代器。应该用来实现 等待对象。例如,asyncio.Future实现此方法以与await表达式兼容。

版本3.5中的新功能。

也可以看看

PEP 492获取有关等待物品的其他信息。

 

3.4.2。协程对象

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

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

改变在3.5.2版本:这是一个RuntimeError对协同程序等待不止一次。

coroutine.send
开始或恢复协程的执行。如果valueNone,则相当于推进返回的迭代器 __await__()。如果value不是None,则此方法委托给send()导致协程挂起的迭代器的方法。结果(返回值, StopIteration或其他异常)与迭代__await__()返回值时的结果相同,如上所述。
coroutine.throw类型[[追溯
在协程中引发指定的异常。throw()如果它有这样的方法,则此方法委托给迭代器的方法,该方法导致协程挂起。否则,在暂停点处引发异常。结果(返回值,StopIteration或其他异常)与迭代__await__()返回值时的结果相同,如上所述。如果协程中没有捕获异常,它会传播回调用方。
coroutine.close
使协程自行清理并退出。如果协程被挂起,这个方法首先委托close() 迭代器的方法,如果它有这样的方法,会导致协程挂起。然后它GeneratorExit在悬挂点上升,导致协程立即自我清理。最后,协程标记为已完成执行,即使它从未启动过。

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

 

3.4.3。异步迭代器

一个异步迭代器可以在它的调用异步代码__anext__的方法。

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

object.__aiter__自我
必须返回异步迭代器对象。
object.__anext__自我
必须返回等待,导致迭代器的下一个值。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.7中更改:在Python 3.7之前,__aiter__可以返回一个等待 解析为 异步迭代器等待

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

 

3.4.4。异步上下文管理器

一个异步上下文管理器是一个上下文管理器,它能够暂停其执行__aenter____aexit__方法。

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

object.__aenter__自我
这种方法在语义上类似于__enter__(),只有区别在于它必须返回等待
object.__aexit__selfexc_typeexc_valuetraceback 
这种方法在语义上类似于__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] 可能在某些情况下更改对象的类型,在某些控制的条件。但它通常不是一个好主意,因为如果处理不当会导致一些非常奇怪的行为。
[2] __hash__()__iter__()__reversed__(),和 __contains__()方法,对这种特殊处理; 其他人仍会提出一个TypeError,但也可能依靠None不可赎回的行为来做。
[3] “不支持”这里意味着该类没有这样的方法,或者方法返回NotImplementedNone如果要强制回退到右操作数的反射方法,则不要将方法设置为 – 这将反过来具有明显阻止此类回退的相反效果 。
[4] 对于相同类型的操作数,假设如果非反射方法(例如__add__())失败则不支持操作,这就是不调用反射方法的原因。