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 = 1
, a
和 b
可能引用或不引用值为1的同一对象,具体取决于实现,但在 c = []; d = []
, c
和 d
保证引用两个不同的、唯一的、新创建的空列表。(注意 c = d = []
将同一对象赋给两个对象 c
和 d
)
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
) 这些都代表了真理的价值观——假与真。表示值的两个对象
False
和True
是唯一的布尔对象。布尔类型是整数类型的子类型,布尔值在几乎所有上下文中的行为都与值0和1类似,但当转换为字符串时,字符串"False"
或"True"
分别返回。
整数表示规则旨在对涉及负整数的移位和屏蔽操作给出最有意义的解释。
- 整数 (
numbers.Real
(float
)这些表示机器级双精度浮点数。您接受了底层机器体系结构(和C或Java实现)的作用,以便接受溢出和处理。python不支持单精度浮点数;通常使用这些浮点数的原因是节省了处理器和内存的使用量,而在python中使用对象的开销则相形见绌,因此没有理由用两种浮点数使语言复杂化。
numbers.Complex
(complex
)它们将复数表示为一对机器级双精度浮点数。同样的警告也适用于浮点数。复数的实部和虚部
z
可以通过只读属性检索z.real
和z.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*k
, n>=
0
和 i<=
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()
构造函数。字节数组除了是可变的(因此是不可变的),还提供了与不可变的相同的接口和功能。bytes
物体。
扩展模块
array
提供可变序列类型的附加示例,以及collections
模块。
- 集合类型
这些表示无序的、有限的、唯一的、不变的对象集。因此,它们不能被任何下标编入索引。但是,它们可以迭代,并且内置函数
len()
返回集合中的项数。集合的常见用途是快速成员资格测试,从序列中删除重复项,以及计算数学运算,如交集、并集、差分和对称差分。对于集合元素,相同的不可变规则适用于字典键。请注意,数字类型遵守数字比较的常规规则:如果两个数字比较相等(例如,
1
和1.0
,一个集合中只能包含其中一个。当前有两种内部集类型:
- 集合
它们代表一个可变的集合。它们是由内置的
set()
然后可以通过多种方法进行修改,例如add()
.- 冻结集
它们代表一个不变的集合。它们是由内置的
frozenset()
构造函数。因为冻结集是不变的 hashable ,它可以再次用作另一个集合的元素或字典键。
- 映射
这些表示由任意索引集索引的有限对象集。下标符号
a[k]
选择索引项k
从地图上a
;这可以在表达式中使用,也可以作为赋值的目标,或者del
声明。内置功能len()
返回映射中的项数。当前有一个内部映射类型:
- 辞典
这些表示由几乎任意值索引的有限对象集。唯一不能作为键接受的值类型是包含列表或字典或其他可变类型的值,这些值是按值而不是按对象标识进行比较的,原因是字典的有效实现要求键的hash值保持不变。用于键的数字类型遵循数字比较的常规规则:如果两个数字比较相等(例如,
1
和1.0
)然后可以互换使用它们来索引同一个字典条目。字典保留插入顺序,这意味着键将按照在字典上按顺序添加的顺序生成。替换现有密钥不会更改顺序,但是删除密钥并重新插入它会将其添加到末尾,而不是保留其原来的位置。
字典是可变的;它们可以由
{{...}}
符号(见第节 词典显示 )扩展模块
dbm.ndbm
和dbm.gnu
提供映射类型的其他示例,以及collections
模块。在 3.7 版更改: 字典在3.6之前的Python版本中没有保留插入顺序。在CPython 3.6中,插入顺序被保留下来,但当时它被认为是一个实现细节,而不是一个语言保证。
- 可调用类型
这些是函数调用操作的类型(请参见第节 调用 )可以应用:
- 用户定义函数
用户定义的函数对象由函数定义创建(请参见第节 函数定义 )应该使用参数列表调用它,该参数列表包含与函数的形参列表相同数量的项。
特殊属性:
属性
意义
__doc__
函数的文档字符串,或
None
如果不可用;不由子类继承。可写的
函数的名称。
可写的
函数的 qualified name .
3.3 新版功能.
可写的
__module__
在中定义函数的模块的名称,或
None
如果不可用。可写的
__defaults__
包含具有默认值的参数的默认参数值的元组,或者
None
如果没有参数具有默认值。可写的
__code__
表示已编译函数体的代码对象。
可写的
__globals__
对保存函数全局变量的字典的引用——定义函数的模块的全局命名空间。
只读
支持任意函数属性的命名空间。
可写的
__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
陈述或结尾脱落,aStopIteration
引发异常,迭代器将到达要返回的值集的结尾。- 协程函数
一种函数或方法,用
async def
被称为 coroutine function . 这样的函数在调用时返回 coroutine 对象。它可能包含await
表达式,以及async with
和async for
声明。也见 协程对象 部分。- 异步生成器功能
一种函数或方法,用
async def
它使用yield
语句称为 asynchronous generator function . 当调用此类函数时,返回一个异步迭代器对象,该对象可用于async for
执行函数体的语句。调用异步迭代器
aiterator.__anext__()
方法将返回 awaitable 它将在等待时执行,直到它使用yield
表达式。当函数执行空return
陈述或结尾脱落,aStopAsyncIteration
引发异常,异步迭代器将到达要生成的值集的结尾。- 内置功能
内置函数对象是围绕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__()
方法,而不是直接更新实例字典。如果类实例具有具有某些特殊名称的方法,那么它们可以假装为数字、序列或映射。见节 特殊方法名称 .
- I/O对象(也称为文件对象)
A file object 表示打开的文件。可以使用各种快捷方式创建文件对象:
open()
内置功能,以及os.popen()
,os.fdopen()
和makefile()
socket对象的方法(也可能是由扩展模块提供的其他函数或方法)。对象
sys.stdin
,sys.stdout
和sys.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
要指示代码对象是否在启用特定功能的情况下编译:bit0x2000
如果函数是在启用未来除法的情况下编译的,则设置;位0x10
和0x1000
在早期版本的python中使用。其他位
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
.启动位置 may 允许通过设置请求每个操作码事件
f_trace_opcodes
到True
. 注意,如果跟踪函数引发的异常转义到被跟踪的函数,这可能导致未定义的解释程序行为。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)
. 除上述情况外,尝试执行操作会在未定义适当方法时引发异常(通常 AttributeError
或 TypeError
)
将特殊方法设置为 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
相反。特别地:
- 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.__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
如果它不实现给定参数对的操作。按照惯例,False
和True
返回以进行成功的比较。但是,这些方法可以返回任何值,因此如果比较运算符用于布尔上下文(例如,在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集合成员的操作,包括set
,frozenset
和dict
.__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 y
和hash(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()
应该返回False
或True
. 如果未定义此方法,__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__
带着参数obj
,name
。
- object.__setattr__(self, name, value)¶
在尝试属性分配时调用。调用它而不是普通机制(即将值存储在实例字典中)。 name 是属性名, value 是要分配给它的值。
如果
__setattr__()
要分配给实例属性,它应该使用相同的名称调用基类方法,例如,object.__setattr__(self, name, value)
.引发一个 auditing event
object.__setattr__
带着参数obj
,name
,value
。
- object.__delattr__(self, name)¶
类似于
__setattr__()
但属性删除而不是赋值。只有当del obj.name
对对象有意义。引发一个 auditing event
object.__delattr__
带着参数obj
,name
。
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__ (只应包含 额外的 插槽)。
如果一个类定义了一个同样在基类中定义的槽,那么由基类槽定义的实例变量是不可访问的(除非直接从基类中检索其描述符)。这使得程序的含义未定义。将来,可能会添加一个支票以防止出现这种情况。
任何非字符串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
类定义行中的关键字参数,或从包含此类参数的现有类继承。在下面的示例中,两者都是 MyClass
和 MySubclass
是的实例 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()
.
成员资格测试操作员 (in
和 not 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.__index__(self)¶
调用以实现
operator.index()
以及每当python需要无损地将数值对象转换为整数对象(如切片或内置bin()
,hex()
和oct()
功能)。此方法的存在表明数值对象是整数类型。必须返回整数。如果
__int__()
,__float__()
和__complex__()
没有定义相应的内置函数int()
,float()
和complex()
回到__index__()
.
3.3.9. 使用语句上下文管理器¶
A context manager 是一个对象,它定义在执行 with
语句。上下文管理器处理执行代码块所需的运行时上下文的入口和出口。上下文管理器通常使用 with
声明(在第节中描述 这个 with 陈述 ,但也可以通过直接调用它们的方法来使用。
上下文管理器的典型用途包括保存和恢复各种全局状态、锁定和解锁资源、关闭打开的文件等。
有关上下文管理器的详细信息,请参阅 上下文管理器类型 .
- object.__exit__(self, exc_type, exc_value, traceback)¶
退出与此对象相关的运行时上下文。参数描述导致上下文退出的异常。如果上下文在没有异常的情况下退出,则所有三个参数都将
None
.如果提供了一个异常,并且该方法希望抑制该异常(即防止它被传播),那么它应该返回一个真值。否则,异常将在退出此方法时正常处理。
注意
__exit__()
方法不应重新引发传入的异常;这是调用方的责任。
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 objects 是 awaitable 物体。协同程序的执行可以通过调用 __await__()
并对结果进行迭代。当协同程序完成执行并返回后,迭代器将引发 StopIteration
和例外情况 value
属性保存返回值。如果协同程序引发异常,则由迭代器传播。协程不应直接引发未处理的 StopIteration
例外情况。
协程也有下面列出的方法,这些方法类似于生成器的方法(参见 生成器迭代器方法 )但是,与生成器不同,协同程序不直接支持迭代。
在 3.5.2 版更改: 这是一个 RuntimeError
不止一次的等待。
- coroutine.send(value)¶
开始或继续执行协同程序。如果 value 是
None
,这相当于前进由返回的迭代器__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__()
--失败,则不支持整体操作,所以不调用反射的方法。