python基础教程之数据模型
3. 数据模型¶
3.1 对象、值和类型¶
对象是Python对数据的抽象。Python程序中所有数据都由对象或对象之间的关系表示。(合理且与冯.诺依曼的“存储程序计算机”模型一致,代码也由对象表示。)
每个对象都有一个ID,一个类型和一个值。对象一旦建立,它的ID永远不会改变;你可以认为它是该对象在内存中的地址。‘is’操作符比较两个对象的ID;id()函数返回一个表示对象ID 的整数(当前实现为对象的地址)。对象的类型也是不可变的。[1]对象的类型决定了对象支持的操作(例如,“它有长度吗?”)同时也定义了该种类型对象的可能的值。type()函数返回对象的类型(它本身也是一个对象)。某些对象的值可以改变。值可以改变的对象称为可变的;一旦建立,值就不可以改变的对象称为不可变的。(包含可变对象引用的不可变容器对象在可变对象改变时是可以改变的;但容器仍被看作是不可变的, 因为它所包含的对象集合是不能变的。所以不可变对象与值不可变不是完全一样的,它更加微妙。)一个对象的可变性由它的类型决定;例如,数值、字符串和元组是不可变的,而字典和列表是可变的。
对象不可以显式地销毁;但是当它们不可用时可能被当作垃圾回收。具体的实现可以推迟垃圾回收或完全忽略它 — 这是垃圾回收如何实现的质量问题,只要依然能访问的对象不被回收。
CPython 实现细节:CPython 当前使用引用计数机制与(可选的)循环连接垃圾延迟检测机制,一旦对象变得不可访问,它将收集其中大部分,但是不保证收集包含循环引用的垃圾。参考gc 模块的文档可以获得控制循环垃圾回收的信息。其它实现的行为与之不同并且CPython 的实现将来也可能会变化。不要依赖对象不可访问后会立即终结(例如:永远关闭文件)。
注意使用具体实现的跟踪和调试工具可能会保持正常情况下可以回收的对象一直存活。还要注意使用‘try…except’语句捕获异常也可能保持对象一直存活。
有些对象包含“外部”资源的引用,例如打开的文件或窗口。可以理解在对象被当作垃圾回收时这些资源也被释放,但因为垃圾回收不保证一定发生,这样的对象也提供显式的方法释放外部资源,通常是close()方法。强烈建议程序显式关闭这些对象。‘try…finally’语句提供了一个便利的方式来做这件事。
有些对象包含其它对象的引用;它们叫做容器。容器的例子有元组,列表和字典。引用是容器的值的一部分。大多数情况下,当我们谈到一个容器的值时,我们是指值,而不是所包含的对象的ID;然而,当我们谈论容器对象的可变性的时候,就只是指被直接包含的对象的ID。因此,如果一个不可变对象(如元组)包含了一个可变对象的引用,那么当这个可变对象的值改变时它的值也发生改变。
对象的类型几乎影响对象的所有行为。在某种意义上甚至重要到影响对象的识别:对于不可变对象,计算新值的运算符可能实际上返回的是一个已存在的具有相同类型和值的对象的引用,而对于可变对象,这是不允许的。例如,在a = 1; b = 1之后,a 和b可能是或者可能不是引用同一个值为1的对象,这依赖于实现,但c = []; d = []之后,c 和d可以保证是引用两个不同的、唯一的、新创建的空列表。(注意c = d = []是把相同的对象赋给c 和d。)
3.2 标准类型的层次结构¶
以下是一份Python 内建类型的清单。扩展模块(无论是用C、Java还是用其它语言编写,依赖于具体实现)可以定义额外的类型。未来版本的Python 可能在这个类型层次结构中增加其它类型(例如:有理数、高效存储的整数数组,等等)。
下面有些类型的描述包含一个列出“特殊属性”的段落。这些属性提供访问具体的实现而不是作为一般的目的使用。它们的定义在未来可能会改变。
- None
-
这种类型只有一个值。只有一个对象具有这个值。这个对象通过内建名字None访问。它在许多情况下用来表示没有值,例如,没有显式返回任何内容的函数会返回它。它的真值为假。
- NotImplemented
-
这种类型只有一个值。只有一个对象具有这个值。这个对象通过内建名字NotImplemented访问。如果数值方法和复杂的比较方法没有为提供的操作数实现某种运算,它们可能返回这个值。(解释器会尝试反射的操作,或者其它退化的操作,依赖于具体的运算符。)它的真值为真。
- Ellipsis
-
这种类型只有一个值。只有一个对象具有这个值。这个对象通过内建名字Ellipsis访问。它用于指示切片中出现的...语法。它的真值为真。
- numbers.Number
-
它们由数值字面量生成或者由算术运算符和内建的算术函数作为结果返回。数值对象是不可变的;一旦创建,它们的值永远不会改变。Python 的数值和数学上的数字关系当然是非常密切的,但受到计算机数值表达能力的限制。
Python 区分整数、浮点数和复数:
- numbers.Integral
-
它们表示数学上的整数集中的元素(包括正数和负数)。
有三种类型的整数:
- 普通整数
-
它们表示在-2147483648 至2147483647 范围之间的数。(这个范围可能会在本地机器字较大的机器更大些,但不会更小。)如果某个运算的结果超出这个范围,结果会以长整数正常返回(在某些情况下,会抛出异常OverflowError)。对于以移位和掩码为目的的运算,整数采用32位或更多位的二进制补码形式,并且不会对用户隐藏任何位。(就是说,所有4294967296个不同的比特组合对应于不同的值)。
- 长整数
-
长整数的表示的数值范围没有限制,只受限于可用的(虚拟内存)内存。对于以移位和掩码为目的的运算,长整数采用二进制的形式,负数用二进制补码形式表示,给人的错觉是一个符号位向左无限扩展的字符串。
- 布尔值
-
布尔值表示假和真的真值。表示False 和True 的两个对象是仅有的布尔对象。布尔类型是普通整数的子类型,布尔值的行为在几乎所有环境下分别类似0和1,例外的情况是转换成字符串的时候分别返回字符串"False" 或"True"。
整数表示法的规则意在让负数的移位和掩码运算具有最有意义的解释,并且在普通整数和长整数之间转换时具有最少的意外。任何运算,只要它产生的结果在整数域之中,那么在长整数域或混合运算时将产生相同结果。域之间的转换对程序员是透明的。
- numbers.Real (float)
-
这种类型表示机器级别的双精度浮点数。你受底层的机器体系结构(和C或者Java的实现)控制接受的范围和溢出处理。Python不支持单精度浮点数;使用它的原因通常是节省处理器和内存的使用,但是相比Python中对象使用的开销是微不足道的,因此没有必要支持两种浮点数使语言变的复杂。
- numbers.Complex
-
这种类型以一对机器级别的双精度浮点数表示复数。单精度浮点数同样可以用于复数类型。复数z的实部和虚部可以通过只读属性z.real和z.imag获得。
- 序列
-
这种类型表示有限的顺序集合,用非负数索引。内建的函数len() 返回序列的元素个数。当序列的长度为n时,索引集合包含数字0, 1, …, n-1。序列a的元素i 的选择使用a[i] 。
序列也支持切片:a[i:j]选择索引k满足i <= k < j的所有元素。作为表达式使用的时候,切片是一个相同类型的序列。这隐含着索引会从零开始重新计数。
某些序列还支持带有第三个“步长”参数的“扩展切片”:a[i:j:k] 选择a 中所有索引为x的 的元素,x = i + n*k, n >= 0 且i <= x < j。
序列依据它们的可变性分为:
-
不可变序列
不可变序列类型的对象一旦创建便不可改变。(如果这个对象包含其它对象的引用,这些引用的对象可以是可变的并且可以改变;然而不可变对象直接引用的对象集合不可改变。)以下类型是不可变序列:
- 字符串
-
字符串的元素是字符。没有单独的字符类型;字符用一个元素的字符串表示。字符以(至少)8 比特的字节表示。内建函数chr() 和ord()在字符和表示字节数值的非负整数之间转换。值在0-127 之间的字节通常表示相应的ASCII 值,但是对值的解释由程序决定。字符串数据类型也用于表示字节的数组,例如,保存从文件中读取的数据。
(在原生字符集不是ASCII 的系统上,字符串在内部可以使用EBCDIC 表示,只要函数chr() 和ord() 实现ASCII 和EBCDIC 之间的映射并且字符串的比较保留ASCII 顺序。或者可能有人能够提出一个更好的规则?)
- Unicode
-
Unicode 对象的元素是Unicode 编码单元。一个Unicode 编码单元由一个元素的Unicode 对象表示并且可以保持16位或者32位的值表示一个Unicode 序数。(序数的最大值在sys.maxunicode中给出,并依赖Python 在编译的时候是如何配置的)Unicode 对象中可以表示代理对,并被当作两个单独的元素。内建的函数unichr() 和ord()在编码单元和表示定义在Unicode 标准3.0中Unicode 序数的非负整数之间转换。和其它编码之间相互转换可以通过Unicode 方法encode() 和内建的函数unicode()。
- 元组
-
元组的元素可以是Python 的任何对象。两个或多个元素的元组由逗号分隔的一连串表达式形成。一个元素的元组(单元素集)可以在一个表达式的后面附加一个逗号形成(一个表达式自身不会形成一个元组,因为圆括号必须可以用来分组表达式)。一个空的元组可以由一个空的圆括号对形成。
- 可变序列
-
可变序列在生成之后可以修改。下标和切片表示法可以用于赋值和del (delete)语句的对象。
当前有两种内建的可变序列类型:
- 列表
-
列表的对象可以是Python 任何对象。列表由在方括号中放置一个逗号分隔的一连串表达式形成。(注意生成长度为0或1的列表没有特殊的情形。)
- 字节数组
-
一个字节数组对象是一个可变的数组。它们由内建的bytearray() 构造函数创建。除了可变性(因此不可哈希),字节数组提供和不可变字节对象同样的接口和功能。
扩展模块array提供另外一个可变序列类型的例子。
-
- 集合类型
-
这种类型表示无序的、有限的集合,集合中的元素是唯一的、不可变的对象。正因如此,它们不可以被任何下标索引。然而,它们可以迭代,内建函数len()返回集合中元素的个数。集合常见的用途有快速成员关系检测、从序列中删除重复元素和计算数学运算例如交集、并集、差集和对称差集。
集合的元素与字典的键一样,都适用不可变规则。注意,数值类型遵循正常的数值比较规则:如果两个数字相等(例如,1 和1.0),其中只有一个可以包含在集合中。
当前有两种内建的集合类型:
- 集合
- 固定集合
-
这种类型表示不可变集合。它们由内建函数frozenset()构造器创建。因为固定集合不可变且可以哈希,它可以作为另外一个集合的元素或者字典的键。
- 映射
-
这种类型表示由任意索引集合作索引的有限对象集合。下标表示法a[k] 从映射a 中选择由k 索引的元素;它可以用在表达式中并作为赋值或del语句的目标。内建函数len()返回映射中元素的个数。
当前只有一个内建映射类型:
- 字典
-
这种类型表示几乎可以由任何值索引的有限对象集合。仅有的不可以作为键值的是包含列表或者字典或者其它可变类型的值(这些类型通过值而不是对象ID比较),原因是字典的高效实现要求键的哈希值保持常量。注意,数值类型遵循正常的数值比较规则:如果两个数字相等(例如,1 和1.0),那么它们可以互换地使用来索引同一个字典入口。
字典是可变的;它们可以通过{...} 表示法创建(参考Dictionary的显示一节)。
- 可调用类型
-
这是一种可以使用函数调用操作(参考Calls一节)的类型:
- 用户定义的函数
-
用户定义的函数对象由函数定义创建(参见函数定义一节)。它调用时参数列表的元素个数应该和函数的形式参数列表相同。
特殊属性:
属性 含义 __doc__
func_doc函数的文档字符串,如果没有就为None。 可写 __name__
func_name函数的名字。 可写 __module__ 函数定义所在的模块名,如果没有就为None。 可写 __defaults__
func_defaults为具有默认值的参数保存默认参数值的元组,如果没有参数具有默认值则为None。 可写 __code__
func_code表示编译后的函数体的代码对象。 可写 __globals__
func_globals保存函数全局变量的字典的引用 — 函数定义所在模块的全局命名空间。 只读 __dict__
func_dict支持任意函数属性的命名空间。 可写 __closure__
func_closureNone 或者包含函数自由变量绑定的元组。 只读 大部分标有“可写”的属性会检查所赋值的类型。
版本2.4中的变化:func_name 成为如今可写的属性。
版本2.6中的变化:引入双下划线属性__closure__, __code__, __defaults__, 和__globals__作为对应的func_*属性的别名以向前兼容Python 3。
函数对象同样支持获取和设置任意属性,这可以用来附加元数据到函数中。常规属性可以用点号表示法获取和设置。注意当前的实现只在用户定义的函数上支持函数属性。未来可能支持内建函数上的函数属性。
函数定义的额外信息可以从它的代码对象中获取;参见下面内部类型的描述。
- 用户定义的方法
-
用户定义的方法将类、类的实例(或者None)和任何可调用对象(通常是一个用户定义的函数)结合起来。
特殊的只读属性:im_self指类实例对象,im_func指函数对象;im_class对于绑定的方法指im_self的类,对于未绑定的方法指方法所在的类;__doc__指方法的文档(与im_func.__doc__相同);__name__指方法的名字(与im_func.__name__一样);__module__指方法定义所在模块的名字,如果没有则为None。
版本2.2中的变化:im_self过去指的是定义方法的类。
版本2.6中的变化:为了Python 3向前的兼容性,im_func也可以使用__func__访问,im_self可以使用__self__访问。
方法也支持访问(但不能设置)底层函数对象的任何函数属性。
用户定义的方法对象可能在获取类的一个属性的时候创建(可能通过类的一个实例),如果那个属性是用户定义的函数对象或者一个未绑定的用户方法对象或者一个类方法对象。如果那个属性是用户定义的方法对象,只有类和存储在原始方法对象中的类或其子类相同,才会创建一个新的方法对象;否则,使用初始的方法对象。
如果用户定义的方法是通过从类中获取用户定义的方法创建,它的im_self为None并且方法对象称为未绑定的。如果是通过从类的某个实例中获取用户定义的方法创建,它的its im_self是那个实例,且方法对象称为绑定的。在任一种情况下,新方法的im_class 属性为那个类,im_func 属性为原始的函数对象。
如果用户定义的方法是通过从类或者实例中获取另外一个方法对象创建,新的实例的im_func 属性不是原始的方法对象而是它的im_func 属性,除此之外其行为和处理函数对象时一样。
如果用户定义的方法对象通过从类或者实例中获取类方法对象创建,它的im_self 属性为类本身,且它的im_func 属性为类方法底层的函数对象。
当调用未绑定的方法对象时,调用的是底层的函数(im_func),有个限制是第一个参数必须是合适的类(im_class) 或其子类的实例。
当调用绑定的用户定义方法时,调用的是底层的函数(im_func),同时在参数列表的前面插入类的实例(im_self)。例如,当C 是一个类,包含函数f() 的定义,并且x 是C 的一个实例,调用x.f(1) 等同于调用C.f(x, 1)。
如果用户定义的方法对象继承自类方法对象,存储在im_self中的“类实例” 实际上将是类本身,因此调用x.f(1) 或者C.f(1) 等同于调用f(C,1),f是底层的函数。
注意从函数对象到(非绑定的或者绑定的)方法对象的转换在每次从类或对象获取属性时都会发生。在某些情况下,一个富有成效的优化是将属性赋值给一个局部变量然后调用那个局部变量。还要注意这种转换只有用户定义的函数会发生;其它可调用的对象(和所有不可调用的对象)在获取时不会转换。同样要注意如果用户定义的函数是类实例的属性不会被转换为绑定的方法;这种转换只 发生在函数是类的属性的时候。
- 生成器函数
-
使用yield 语句(参考yield 语句一节)的函数或方法称为生成器函数。这种函数,在调用的时候,总是返回一个可以用来执行函数体迭代器对象:调用迭代器的next() 方法将导致函数执行直到它使用yield语句提供一个值。当函数执行return 语句或者到达结束位置,会引发一个StopIteration 异常且迭代器将到达要返回的数据集合的末尾。
- 内建的函数
-
内建的函数对象是C函数的封装。内建函数的例子有len() 和math.sin()(math 是标准的内建模块)。参数的数目和类型由C函数决定。特殊的只读属性:__doc__ 指函数的文档字符串,如果没有则为None;__name__ 指函数的名字;__self__ 被设置为None(看看下一个属性);__module__ 指函数定义所在模块的名字,如果没有则为None。
- 内建的方法
-
它和内建函数是完全不同的伪装,这次包含一个传递给C函数的对象作为隐式的参数。一个内建方法的例子是alist.append(),假设alist 是一个列表对象。在这种情况下,特殊的只读属性__self__ 设置为alist 对象。
- 类类型
- 类类型,或者“新式的类”是可调用的。这些对象通常作为工厂创建它们自己的实例,但是覆盖__new__()方法的类类型可以有所变化。调用的参数传递给__new__(),且在典型情况下再传递给__init__() 来初始化新的实例。
- 经典的类
-
下面讲述类对象。当调用一个类对象时,会创建并返回一个新的类实例(下面也会讲述)。这隐含着会调用类的__init__() 方法,如果它有的话。任何参数都会传递给__init__() 方法。如果类没有__init__() 方法,那么调用类必须不带参数。
- 类的实例
- 下面讲述类的实例。类的实例只有当类具有__call__() 方法是才可以调用;x(arguments) 是x.__call__(arguments) 的简写。
- 模块
-
模块使用import 语句导入(参见import 语句一节)。模块对象有一个用字典对象实现的命名空间(这就是模块中定义的函数的func_globals 属性引用的字典)。属性的引用被转换成查询这个字典,例如m.x 等同于m.__dict__["x"]。模块对象不包含初始化模块的代码对象(因为一旦初始化完成,它不会再需要了)。
属性的赋值会更新模块的命名空间字典,例如m.x = 1 等同于m.__dict__["x"] = 1。
特殊的只读属性:__dict__ 指模块字典对象形式的命名空间。
CPython 实现细节:由于CPython 清除模块字典的方式,当模块离开作用域之外时,模块的字典将被清除即使字典仍然被引用。为了避免这一行为,复制一份模块的字典或者在直接使用模块字典的时候把模块保留着。
预定义的(可写)属性:__name__ 为模块的名字;__doc__ 指模块的文档字符串,如果没有则为None;__file__ 为模块加载的文件路径,如果它是从文件中加载的话。某些类型的模块可能没有__file__ 属性,例如与解释器静态连接的C 模块;静态连接的C 模块是不存在的;对于从共享的库中动态加载的扩展模块,它是共享的库文件的路径。
- 类
-
类类型(新式类)和类对象(旧式/经典类)都由类定义创建(参见Class definitions 一节)。类有一个用字典对象实现的命名空间。类属性的引用被转换为这个字典的查询,例如,C.x 被转换为C.__dict__["x"](尽管新式类有若干特别的钩子允许其它方式定位属性)。当属性名在字典中没有找到时,会继续在基类中搜索。对于旧式的类,搜索是深度优先,按基类出现的顺序从左向右。新式的类使用更加复杂的C3 MRO,它的行为在即使出现‘diamond’继承时也是正确的,这时有多个继承路径可以回溯到同一个祖先。新式类使用的C3 MRO 的更多细节可以在2.3 版的文档中找到 http://www.python.org/download/releases/2.3/mro/。
当类属性引用(例如类C)产生一个用户定义的函数对象或者未绑定的用户定义的方法对象,该方法对象相关联的的类是C或者它的基类,它被转换成一个未绑定的用户定义的方法对象,该方法对象的im_class 属性是C。当它产生一个类方法对象时,它被转换成一个绑定的用户定义的方法对象,它的im_self 属性是C。当它产生一个静态的方法对象时,它被转换成由该静态方法对象封装的对象。在实现描述符 一节可以看到另外一种方式,从类中获取的属性可能与真正包含在__dict__中的不同(注意只有新式的类支持这种描述符)。
类属性的赋值将更新该类的字典,永远不会更新其基类的字典。
可以调用类对象(参考上面)产生一个类的实例(参考下面)。
特殊的属性:__name__ 是类的名字;__module__ 是类定义所在的模块名;__dict__ 是包含类命名空间的字典;__bases__ 是包含类的基类的元组,顺序为它们在基类列表中出现的顺序;__doc__ 是类的文档字符串,如果没有定义则为None。
- 类的实例
-
类的实例通过调用类对象创建(参见上文)。类的实例有一个用字典实现的命名空间,它是搜索属性引用的第一个地方。如果属性在那里没找到,但实例的类中有那个名字的属性,则继续在类的属性中查找。如果找到的是一个用户定义函数对象或者未绑定的用户定义的方法对象,而该方法对象关联的类是实例的类(称它为C)且属性引用是它或者其基类,那么它被转换成一个绑定的用户定义的方法对象,这个方法对象的im_class 属性为C且im_self属性为这个实例。 静态方法和类方法对象也会转换,就像它们已经从类C 中获取过一样;参见上文“类”。在实现描述符 一节可以看到另外一种方式,通过实例获取类的属性可能与真正包含在类的__dict__中的不同。如果没有找到相应的类属性,并且对象的类有一个__getattr__()方法,那么这个方法会被调用以满足搜索。
属性的赋值和删除会更新实例字典,永远不会是类的字典。如果类有一个__setattr__() 或者__delattr__() 方法,那么会调用这个方法而不是直接更新实例的字典。
如果有特定名字的方法的定义,类实例可以伪装成数值,序列或者映射类型。参见特殊方法的名字 一节。
- 文件
-
文件对象表示打开的文件。文件对象可以由内建函数open(),os.popen(),os.fdopen() 和socket 对象的makefile()方法创建(或者其它扩展模块提供的函数或方法)。sys.stdin、sys.stdout 和sys.stderr文件对象 分别初始化为解释器的标准输入、输出和错误流。文件对象完全的文档参见文件对象 一节。
- 内部类型
-
解释器内部使用的一些类型会暴露给用户。它们的定义可能跟随解释器以后的版本变化,但是为了完整性,这里还是会提及。
- 代码对象
-
代码对象表示byte-compiled 的可执行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_flags 定义以下的标志位:如果函数使用*arguments 语法接收任意数目的位置参数,则置位成0x04 ;如果函数使用**keywords 语法接收任意的关键字参数,则置位成0x08;如果函数式一个产生器,则置位成0x20。
未来功能的声明(from __future__ import division)同样使用co_flags 中的位来指明代码对象的编译是否启用某个特别的功能;如果函数的编译启用了未来的除法,则置位成0x2000。在早期的Python 版本中,还使用过0x10 和0x1000 标志位。
co_flags 中的其它位保留作内部使用。
如果代码对象表示一个函数,co_consts 中的第一个元素是函数的文档字符串,如果没有定义文档字符串则为None。
- 帧对象
-
帧对象表示执行的帧。它们可以出现在回溯对象中(参见下面)。
特殊的只读属性:f_back 指向堆栈中的前一帧(朝调用的方向),如果这是堆栈最底部的帧则为None。f_code 是帧中正在执行的代码对象;f_locals 是用于查询局部变量的字典;f_globals 用于全局变量;f_builtins 用与内建的(固有的)名字;f_restricted 是指示函数是否以受限的执行模式执行的标志;f_lasti 给出精确的指令(是一个代码对象的字节码字符串索引)。
特殊的可写属性:f_trace,如果不为None,则是源代码开始调用的函数(用于调试器);f_exc_type,f_exc_value,f_exc_traceback 表示在父帧中最后发生的异常,只要在当前帧中曾经发生过异常(在所有其它情况下它们为None);f_lineno 帧当前的行号 — 从跟踪函数的内部写入这个值将跳转到指定的行(只用于最底部的帧)。调试器通过写入f_lineno可以实现跳转命令(又叫做Set Next 语句)。
- 回溯对象
-
回溯对象表示一个异常的栈回溯。回溯对象在发生异常时创建。当搜索异常处理器展开执行的栈时,在每一个展开层级,会把一个回溯对象会插入到当前回溯对象的前面。当进入异常处理器时,程序便可以访问栈回溯。(参见try 语句 一节。)它可以通过sys.exc_traceback,或者sys.exc_info() 返回的元组的第三个元素得到。倾向于后面一种方式,因为当程序使用多线程时,它也可以正确工作。当程序没有保护合适的处理器时,栈回溯被写到(格式很漂亮)标准错误流;如果解释器是交互式的,用户还可以通过sys.last_traceback 得到它。
特殊的只读属性:tb_next 是栈回溯中的下一个层级(朝异常发生的那个帧的方向),如果没有下一级则为None。tb_frame 指向当前层级的执行帧;tb_lineno 给出异常发生的行号;tb_lasti 指示精确的指令。如果异常发生在没有匹配的except 字句或者带有finally 字句的try 语句中,回溯中行号和最后的指令可能与帧对象的行号不同。
- 切片对象
-
切片对象在使用扩展的切片语法时用于表示切片。这种切片使用两个冒号,多切片或者省略号,例如,a[i:j:step],a[i:j, k:l],或a[..., i:j]。它们还可以由内建的slice() 创建。
特殊的只读属性:start 指下边界;stop 指上边界;step 指步长;每个属性在省略的情况下都为None。这些属性可以使有任意类型。
切片对象支持下面一种方法:
- slice.indices(self, length)¶
-
这个方法带有一个整数参数length,计算扩展切片的信息,如果切片对象应用到一个长度为length的序列将描述,该切片对象将描述的信息。它返回一个包含三个整数的元组;它们分别是start,stop索引和步长step。没有或者越界的索引以与正规的切片一致的方式处理。
在版本2.3 中新引入。
- 静态方法对象
- 静态方法对象提供一种可以绕过上面所讲的函数对象到方法对象转换的方法。静态方法对象可以是其它任何对象的封装,通常是用户定义方法。 当从一个类或者类实例获取静态方法对象时,返回的对象事实是封装过的对象, 这种对象不可以再进行其它转换。静态方法对象本身是不可调用的,尽管它所封装的对象通常是可调用的。静态方法对象由内建的staticmethod() 构造器创建。
- 类方法对象
- 类方法对象,就像静态方法对象,是对另一个对象的封装,它改变从类或者类实例中获取对象的方式。类方法对象在获取时的行为在上面有所描述,见“用户定义的方法”。类方法对象由内建的classmethod() 构造器创建。
3.3. 新式类和经典类¶
类和实例有两种类型:旧式(或者经典)类和新式类。
直到Python 2.1,旧式类是用户唯一可用的类型。(旧式)类的概念和类型的概念没有关系:如果x 是旧式类的实例,那么x.__class__ 表示x 的类,但是type(x) 永远是<type 'instance'>。这反映了一个事实:所有的旧式实例,不管它们的类是什么,都是用同一个内建类型实现的,叫做instance。
新式的类在Python 2.2 中引进以统一类和类型。新式类不多不少就是一个用户定义的类型。如果x 是新式类的实例,那么type(x) 和x.__class__ 通常是相同的。(但不能保证这点 – 新式类的实例允许覆盖x.__class__ 的返回值)。
引入新式类的主要动机是为了提供一个带有完整元模型的统一的对象模型。同时它还有许多实际的好处,比如继承大部分内建的类型和引入计算属性功能的“描述符”。
由于兼容性的原因,类在默认情况下仍然是旧式的。新式类通过指明另外一个新式类(例如 type)作为父类创建,如果不需要其它父类,也可以是“最顶级的类型” object。除了type() 返回的内容不同,新式类和旧式类的行为在许多重要的细节上都有不同。其中一些变化对于新对象模型(the new object model)是基础性的,比如特殊方法调用的方式。其它是对之前由于兼容性不能实现“修复”,比如多继承情况下方法解析的顺序。
虽然这份手册目标是全面地覆盖Python’s 的类机制,但是在遇到新式类时可能仍然缺少某些方面。请参见http://www.python.org/doc/newstyle/ 获得更多的信息源。
旧式类在Python 3中被移除,只留下新式类的语义。
3.4. 特殊方法的名字¶
通过定义具有特殊名字的方法,类能够实现某些由特殊语法调用的操作(例如算术运算、下标和切片)。这是Python 运算符重载的方法,允许类针对语言运算符定义它们自己的行为。例如,如果某个类定义了一个名字为__getitem__()的方法,并且x 是这个类的实例,那么x[i]对于旧式类粗略等价于x.__getitem__(i),对于新式类等价于type(x).__getitem__(x, i)。除非特别说明,当没有定义适当方法时,执行某个操作会抛出异常(一般是AttributeError 或者TypeError)。
在实现一个模拟内建类型的类时,重要的是模拟只需要达到对建模的对象有意义的程度。例如,对于获取单个元素某些序列可以工作,但抽取切片却是没有意义的。(一个例子是W3C的文档对象模型中的NodeList 接口。)
3.4.1. 基本的定制¶
- object.__new__(cls[, …])¶
-
用于创建类cls 的新实例。__new__()是一个静态方法(你不需要显式地这样声明),以实例的类为第一个参数。其余的参数是传递给对象构造表达式(即类的调用)的参数。__new__() 的返回值应该是新的对象实例(通常是cls 的一个实例)。
典型的实现通过使用带有合适参数的 super(currentclass, cls).__new__(cls[, ...]) 调用超类的__new__() 方法创建类的一个新的实例,然后在返回之前有必要地修改新创建的类实例
。
如果__new__() 返回cls 的一个实例,那么新实例的__init__() 方法将以类似__init__(self[, ...]) 的方式被调用,self 是新的实例,其它的参数和传递给__new__() 的参数一样。
如果__new__() 不是返回cls 的实例,那么新实例的__init__() 方法将不会被调用。
__new__() 主要是用来允许继承不可变类型(比如int、str 和tuple)以定制化实例的创建。它也经常在自定义的元类中被覆盖以定制类的创建。
- object.__init__(self[, …])¶
-
在实例(被__new__())创建完成时调用。参数为传递给类构造器表达式的那些参数。如果基类具有__init__()方法,那么继承的类的__init__()方法,如果有的话,必须显式调用它以保证该实例的基类部分的合理初始化;例如:BaseClass.__init__(self, [args...])。构造器的一个特殊限制是不可以返回任何值;这样做将导致运行时抛出一个TypeError。
- object.__del__(self)¶
-
在实例即将销毁时调用。它也叫做析构器。如果一个基类具有一个__del__()方法,继承类的__del__()方法,如果有的话,必须显式地调用它以保证实例的基类部分的正确删除。注意__del__()方法可以(虽然不推荐)通过创建该实例的一个新的引用以推迟它的销毁。它可以在以后该新的引用被删除时调用。不能保证在解释器退出时仍然存在的对象的__del__()被调用。
注
del x不会直接调用x.__del__() — 前者减少一个x的引用,后者只是在x的引用达到零时调用。某些常见的情况可能阻止一个对象的引用变成零:对象间的循环引用(例如,一个双向链表或者一个具有指向父亲和儿子指针的树数据结构);a reference to the object on the stack frame of a function that caught an exception (the traceback stored in sys.exc_traceback keeps the stack frame alive); or a reference to the object on the stack frame that raised an unhandled exception in interactive mode (the traceback stored in sys.last_traceback keeps the stack frame alive). 第一种情形只有显式地破坏这个换才能修复;后两种情形可以通过保存None到sys.exc_traceback或sys.last_traceback中解决。已经成为垃圾的循环引用在循环检测选项启用时检测(默认是打开的),但是只有当没有Python级别的__del__()方法时才会被清除。参见gc模块的文档以获得关于循环检测器如何处理__del__()方法的更多信息,特别是garbage值的描述。
警告
由于调用__del__() 方法的环境是不确定的,在它们执行时的异常会被忽略,而会打印警告到sys.stderr。另外,当因为正在删除一个模块而调用__del__()时(例如,当程序执行完毕),__del__()方法引用的其它目标可能已经被删除或者正在销毁的过程中(例如,导入的机器正在关机)。由于这个原因,__del__() 方法应该干需要的尽量少的事情以维持外部的不变量。从1.5版开始,Python保证名字以单个下划线开始的全局变量在其它全局变量删除前从它们的模块中删除;如果没有其它对这样的全局变量的引用操作,这可能帮助假定在调用__del__()方法时刻导入的模块仍然可以访问。
另请参阅-R命令行选项。
- object.__repr__(self)¶
-
由repr()内建函数和字符串转换式(反引号)调用以计算一个对象的“正式”的字符串表示。如果可能,它应该看上去像是一个合法的Python表达式,该表达式能够被用于重新创建一个具有相同值的对象(只要给出合适的环境)。如果不可能,应该返回<...some useful description...>形式的字符串。返回值必须是一个字符串对象。如果一个类定义了__repr__()但没有定义__str__(),那么在请求该类的实例的“非正式”的字符串表示时也将调用__repr__()。
它主要用于调试,所以信息丰富且没有歧义的表示非常重要。
- object.__str__(self)¶
-
由str()内建函数和print语句调用以计算一个对象的“非正式的”字符串表示。与__repr__()不同的是它不需要是一个合法的Python表达式:可以使用更合适或者简明的表示。返回值必须是一个字符串对象。
- object.__lt__(self, other)¶
- object.__le__(self, other)¶
- object.__eq__(self, other)¶
- object.__ne__(self, other)¶
- object.__gt__(self, other)¶
- object.__ge__(self, other)¶
-
2.1版中新增。
这些就是所谓的“多元比较”方法,且优先于下文的__cmp__()调用。操作符和方法名之间的对应关系如下:x<y调用x.__lt__(y),x<=y调用x.__le__(y),x==y 调用x.__eq__(y),x!=y和x<>y调用x.__ne__(y),x>y调用x.__gt__(y),x>=y调用x.__ge__(y)。
一个多元比较方法可以返回单个NotImplemented如果它对应一对参数没有实现该操作。按照惯例,成功的比较返回False和True。然而,这些方法可以返回任意值,所以如果比较操作符在布尔环境中使用(例如,在if语句的条件中),Python将在该值上调用bool()来决定结果是真还是假。
比较操作之间没有隐含的关系。x==y这样的事实并不暗含x!=y为假。于是,当定义__eq__()时,还应该定义__ne__()以便操作符与预期的行为一致。See the paragraph on __hash__() for some important notes on creating hashable objects which support custom comparison operations and are usable as dictionary keys.
这些方法没有参数交换后的版本(用于左侧参数不支持该操作单右侧参数支持时);当然,__lt__()和__gt__()互为对方的反射,__le__()和__ge__()互为对方的反射,__eq__()和__ne__()是它们自己的反射。
多元比较方法的参数永远不会被强制转换。
若要从一个单独的根操作自动产生排序的操作,请参阅functools.total_ordering()。
- object.__cmp__(self, other)¶
-
如果没有定义多元比较(见上文),则调用比较操作。如果self < other,应该返回一个负整数;如果self == other,应该返回零;如果self > other,应该返回一个正整数。如果__cmp__()、__eq__()和__ne__()操作都没有定义,那么类的实例通过对象的ID(“地址”)比较。See also the description of __hash__() for some important notes on creating hashable objects which support custom comparison operations and are usable as dictionary keys. (注:__cmp__()不会传播异常的限制已经自Python 1.5移除。)
- object.__rcmp__(self, other)¶
-
版本2.1中的变化:不再支持。
- object.__hash__(self)¶
-
由内建函数hash()调用和用于哈希后的集合成员的操作,包括set, frozenset和dict。__hash__()应该返回一个整数。唯一要求的性质是比较起来相等的对象具有相同的哈希值;it is advised to somehow mix together (e.g. using exclusive or) the hash values for the components of the object that also play a part in comparison of objects.
如果一个类没有定义__cmp__()和__eq__()方法,它也不应该定义__hash__()操作;如果它定义了__cmp__()或__eq__()方法但没有定义__hash__(),它的实例不可以用于哈希集合中。如果一个类定义的是可变对象且实现了__cmp__()或__eq__()方法,那么它不应该实现__hash__(), 因为可哈希的集合实现要求对象的哈希值是不可变的(如果对象的哈希值改变,那么它将在错误的哈希桶中)。
用户自定义的类默认具有__cmp__()和__hash__()方法;有了它们,所有的对象比较起来都不相等(除非和它们自己比较)且x.__hash__()返回一个从id(x)得到的结果。
Classes which inherit a __hash__() method from a parent class but change the meaning of __cmp__() or __eq__() such that the hash value returned is no longer appropriate (e.g. by switching to a value-based concept of equality instead of the default identity based equality) can explicitly flag themselves as being unhashable by setting __hash__ = None in the class definition. Doing so means that not only will instances of the class raise an appropriate TypeError when a program attempts to retrieve their hash value, but they will also be correctly identified as unhashable when checking isinstance(obj, collections.Hashable) (unlike classes which define their own __hash__() to explicitly raise TypeError).
2.5版中的变化: __hash__() 现在也可以返回一个长整数对象;该32位整数从该对象的哈希值获得。
- object.__nonzero__(self)¶
-
用于实现真值测试和内建操作bool()的调用;应该返回False或True,或者它们的整数值等于0或1。当该方法没有定义时,如果定义了__len__()则调用它,并且如果它的结果非零则认为该对象为真。如果一个类既没有定义__len__()也没有定义__nonzero__(),那么它的所有实例都认为是真。
3.4.2. 自定义属性访问¶
可以定义下面的方法来自定义类实例的属性访问的含义(访问、赋值或者删除x.name)。
- object.__getattr__(self, name)¶
-
当属性查找在通常的地方没有找到该属性时调用(例如,它既不是实例属性也没有在self的类树中找到)。name为属性的名字。该方法应该返回(计算后的)属性值或者抛出一个AttributeError异常。
注意,如果属性可以通过正常的机制找到,则不会调用__getattr__()。(__getattr__()和__setattr__()之间的这种不对称是故意设计的。)这既是出于效率的原因也是因为否则的话__getattr__()将无法访问实例的其他属性。注意,至少对于实例变量,你可以通过不插入任何值到实例的属性字典中(而是将它们插入到另外一个对象中)以伪装完全的控制。参见下文的__getattribute__()方法以获得一种真正完全控制新式类的方式。
- object.__setattr__(self, name, value)¶
-
当尝试给一个属性赋值时调用。它的调用将代替普通的机制(例如,将值存储在实例的字典中)。name是属性的名字,value是将要赋给它的值。
如果__setattr__()想赋值给一个实例属性,它不应该简单地执行self.name = value — 这将导致递归调用它自己。相反,它应该将该值插入到实例属性的字典中,例如,self.__dict__[name] = value。对于新式类,它应该调用基类相同名称的方法,而不是访问实例的字典,例如,object.__setattr__(self, name, value)。
- object.__delattr__(self, name)¶
-
类似__setattr__()但是用于属性的删除而不是赋值。它应该只有在del obj.name对该对象有意义时才实现。
3.4.2.1. 新式类更多的属性访问¶
下面的方法只适用于新式类。
- object.__getattribute__(self, name)¶
-
无条件地调用以实现类实例的属性访问。如果类同时定义了__getattr__(),那么后者将不会被调用除非__getattribute__()显式调用它或者抛出一个AttributeError。该方法应该返回(计算后的)值或者抛出一个AttributeError异常。为了避免在该方法中无限的递归,它的实现应该永远调用基类的同名方法以访问需要的任何属性,例如object.__getattribute__(self, name)。
注
这个方法可能仍然被绕开当通过语言的语法或者内建函数的隐式调用的结果导致的查询特殊方法。参见新式类特殊的方法查找。
3.4.2.2. 实现描述器¶
这些方法仅仅适用于那些包含这些方法的类(所谓的描述类)的一个实例,并出现在一个所有者类中(这些描述必须在所有者的类字典中或是在他自己父类的字典中)在下面的例子当中, “the attribute”指的是在所有者__dict__属性中键值定义的属性
- object.__get__(self, instance, owner)¶
-
调用以获取所有者类的属性(可获得的类属性)或者是该类的一个实例属性(可获得的实例属性)owner is always the owner class, while instance is the instance that the attribute was accessed through, or None when the attribute is accessed through the owner. This method should return the (computed) attribute value or raise an AttributeError exception.
- object.__set__(self, instance, value)¶
-
Called to set the attribute on an instance instance of the owner class to a new value, value.
- object.__delete__(self, instance)¶
-
Called to delete the attribute on an instance instance of the owner class.
3.4.2.3. 调用描述器¶
一般情况下,描述器是一个具有“绑定行为”的对象属性,它的属性访问被描述器协议中的方法:__get__(), __set__()和__delete__()覆盖。如果对象定义了这些方法中的任何一个,则称该对象是一个描述器。
属性访问的默认行为是从一个对象的字典中获取、设置或删除属性。例如,a.x的查找链从a.__dict__['x']开始,然后是type(a).__dict__['x'],接着继续搜寻出元类以外的type(a)的基类。
然而,如果要查找的值是一个定义了某个描述器方法的对象,那么Python可能覆盖默认的行为而调用描述器方法。它在优先链中发生的位置取决于定义了哪个描述器方法已经它们是如何调用的。注意,描述器只为新式对象或类(object()或type()的子类)调用。
描述器调用的起点是一个绑定,a.x。参数如何组装取决于a:
- Direct Call
- The simplest and least common call is when user code directly invokes a
descriptor method: x.__get__(a). - Instance Binding
- If binding to a new-style object instance, a.x is transformed into the call:
type(a).__dict__['x'].__get__(a, type(a)). - Class Binding
- If binding to a new-style class, A.x is transformed into the call:
A.__dict__['x'].__get__(None, A). - Super Binding
- If a is an instance of super, then the binding super(B,
obj).m() searches obj.__class__.__mro__ for the base class A
immediately preceding B and then invokes the descriptor with the call:
A.__dict__['m'].__get__(obj, obj.__class__).
对于实例绑定,描述器调用的优先级取决于定义的描述器方法。描述器可以定义__get__()、__set__()和__delete__()的任何组合。如果它没有定义__get__(),那么访问该属性将返回描述器对象自己除非该对象实例的字典中有一个值。如果描述器定义了__set__()和/或__delete__(),那么它是一个数据描述器;如果都没有定义,则是一个非数据描述器。通常,数据描述器__get__()和__set__()两个都定义,而非数据描述器值定义__get__()方法。定义了__set__()和__get__()的数据描述器始终覆盖实例字典中的重复定义。相反,非数据描述器可以被实例覆盖。
Python的方法(包括staticmethod()和classmethod())实现为非数据描述器。因此,实例可以重新定义并覆盖这些方法。这允许相同类的每个实例个体获得与其它实例不同的行为。
property()函数实现为数据描述器。因此,实例不可以覆盖property的行为。
3.4.2.4. __slots__¶
默认情况下,旧式类和新式类都具有一个存储属性的字典。这对于具有非常多的实例属性的对象非常浪费空间。当创建大量的实例时,空间的消耗可能变得很严重。
在新式类的定义中,该默认行为可以通过定义__slots__覆盖。__slots__声明接受一个实例变量的序列并且在每个实例中只保留为每个变量保存一个值的空间。空间被省下来了是因为不会为每个实例创建__dict__。
- __slots__¶
-
这个类变量可以赋值为一个字符串、可迭代对象或者由实例使用的变量名组成的字符串序列。如果在新式类中定义,__slots__将保留空间给声明的变量且防止为每个实例自动创建__dict__和__weakref__。
2.2版中新增。
使用__slots__的注意事项
-
如果继承自一个没有__slots__的类,该类的__dict__属性将始终可以访问,所以在子类中定义__slots__毫无意义。
-
没有__dict__变量,实例不可以给不在__slots__中定义的新变量赋值。尝试给没有列出的变量名赋值将引发AttributeError。如果需要动态地给新的变量赋值,那么可以在__slots__的声明的字符串序列中增加'__dict__'。
2.3版中的变化:在此之前,添加'__dict__'到__slots__声明中不会使得给没有在实例变量名称序列中列出的新属性赋值。
-
因为每个实例都没有__weakref__变量,定义__slots__ 的类不支持对其实例的弱引用。如果需要支持弱引用,可以在__slots__声明的字符串序列中增加'__weakref__'。
2.3版中的变化:在此之前,添加'__weakref__'到 __slots__声明中并不会支持弱引用。
-
__slots__在类级别上实现,通过为每个变量名创建描述器(Implementing Descriptors)。结果,类属性不可以用于设置__slots__定义的实例变量的默认值;否则,该类属性将覆盖描述器的赋值。
-
__slots__定义的动作只限于它所定义的类。结果,字典将具有__dict__,除非它们也定义了__slots__(必须只能包含额外的slots名称)。
-
如果类定义了一个在基类中定义了的slot,基类slot定义的实例变量将不可访问(除非直接从基类获取它的描述器)。这致使程序的含义无法定义。在未来,可能会增加一个检查来防止这个行为。
-
非字符串形式的可迭代类型可以赋值给__slots__。映射也可以使用;然而,在未来,可能对每个键对应的值赋予特殊的含义。
-
__class__赋值只有在两个类具有相同的__slots__是才工作。
2.6版中的变化:在此之前,__class__赋值将抛出一个错误如果新式类或旧式类具有__slots__。
3.4.3. 自定义类的创建¶
默认情况下,新式类使用type()构建。类定义被读取到一个单独的命名空间,然后类名称的值被绑定到type(name, bases, dict)的结果。
当读取类定义时,如果定义了__metaclass__,那么赋给它的可调用对象将被调用而不再调用type()。这允许类或者函数可以被覆盖,这样可以监控或者改变类创建的过程:
- Modifying the class dictionary prior to the class being created.
- Returning an instance of another class – essentially performing the role of a
factory function.
这些步骤必须在元类的__new__()方法中去做 – 然后可以在这个方法中调用type.__new__()来创建一个具有不同属性的类。下面的例子在创建类之前添加一个新的元素到类的字典中:
class metacls(type):
def __new__(mcs, name, bases, dict):
dict['foo'] = 'metacls was here'
return type.__new__(mcs, name, bases, dict)
当然你也可以覆盖其它的类方法(或者添加新方法);例如在元类中定义一个定制的__call__()方法允许调用该类时的自定义行为,例如不用始终创建一个新的实例。
恰当的元类由下面的过程规则决定:
- 如果存在dict['__metaclass__'],则使用它。
- 否则,如果至少有一个基类,则使用它的元类(首先寻找__class__属性,如果不存在则使用它的类型)。
- 否则,如果存在一个名为__metaclass__的全局变量,则使用它。
- 否则,按照旧的方式,使用经典的metaclass(type.ClassType)。
元类的潜在使用方式没有限制。使用场景包括日志打印、接口检查、自动托管、自动的属性创建、代理、框架和自动的资源加锁/同步。
3.4.4. 自定义实例和子类的检查¶
2.6版中新增。
下面的方法用于覆盖内建函数isinstance()和issubclass()的默认行为。
In particular, the metaclass abc.ABCMeta implements these methods in order to allow the addition of Abstract Base Classes (ABCs) as “virtual base classes” to any class or type (including built-in types), including other ABCs.
- class.__instancecheck__(self, instance)¶
-
如果instance应该被认为是class的(直接或间接)实例,返回真。如果有定义,则在isinstance(instance, class)时调用。
- class.__subclasscheck__(self, subclass)¶
-
如果subclass应该被认为是class的(直接或间接)子类。如果有定义,则在issubclass(subclass, class)时调用。
注意这些方法在类的类型(元类)上查找。它们不可以在真实类中定义为类方法。这与在实例上调用特殊方法的查找一致,只有在这种情况下实例本身也是一个类。
另请参阅
- PEP 3119 – 引入抽象基类
- 包括通过__instancecheck__()和__subclasscheck__()定制isinstance()和issubclass()行为的说明,该功能推动了添加抽象基类(参见abc模块)到该语言中。
3.4.5. 模拟可调用对象¶
- object.__call__(self[, args…])¶
-
当实例作为函数“调用”时调用;如果定义了该方法,则x(arg1, arg2, ...)为x.__call__(arg1, arg2, ...)的简写。
3.4.6. 模拟容器类型¶
可以定义下面的方法来实现容器对象。容器类型通常是序列(例如列表或元组)或映射(比如字典),但是也可以表示其它容器。第一组方法用于模拟一个序列或者一个映射;不同的是,对于序列,允许的键应该是整数k,其中0 <= k < N,N是序列或者切片对象的长度,它们定义一个范围的元素。(为了向后兼容,也可以定义__getslice__()方法(见下文)来处理简单的切片,扩展的切片不可以。)同时建议映射类型提供keys(), values(), items(), has_key(), get(), clear(), setdefault(), iterkeys(), itervalues(), iteritems(), pop(), popitem(), copy()和 update() 方法,且行为与Python标准字典对象相似。The UserDict 模块提供一个DictMixin类来帮助从一个基础的__getitem__(), __setitem__(), __delitem__()和keys()集合中创建这些方法。不可变序列应该提供append(), count(), index(), extend(), insert(), pop(), remove(), reverse()和sort(),就像Python标准的列表对象。最后,序列类型应该通过定义下文描述的__add__(), __radd__(), __iadd__(), __mul__(), __rmul__()和__imul__()实现加法(表示连接)和乘法(表示重复);它们不应该定义__coerce__()或其它数值操作符。建议映射和序列都实现__contains__()方法以允许使用高效的in操作符;对于映射,in应该等同于has_key();对于序列,它应该搜遍全部的值。还要进一步建议映射和序列都实现__iter__()方法以允许遍历容器的高效迭代;对于映射,__iter__()应该与iterkeys()相同;对于序列,它应该迭代全部的值。
- object.__len__(self)¶
-
调用它以实现内建的函数len()。应该返回对象的长度,一个>=0的整数。另外,一个没有定义__nonzero__()方法且__len__()方法回执零的对象在布尔上下文中被认为是假。
- 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.__iter__(self)¶
-
该方法在为一个容器请求一个迭代器时调用。该方法应该返回一个新的迭代器对象,它可以迭代容器中的所有对象。对于映射,它应该在容器的键上迭代,且应该也可以像iterkeys()一样可以访问。
迭代器对象也需要实现该方法;它们要求返回它们自己。关于迭代器对象的更多信息,请参见迭代器类型。
- object.__reversed__(self)¶
-
由内建的reversed()调用以实现反向的迭代。它应该返回一个新的迭代器对象,以反向的顺序迭代容器中的对象。
如果没有提供__reversed__()方法,内建的reversed()将退化成使用序列协议(__len__()和__getitem__())。支持序列协议的对象应该只有在它们能提供一个比reversed()提供的实现更高效时才提供__reversed__()。
2.6版中新增。
成员测试操作符(in和not in)通常实现成一个序列的迭代。然而,容器对象可以提供下面的特殊方法,它具有更有效的实现且不要求对象是一个序列。
- object.__contains__(self, item)¶
-
调用它以实现成员测试操作符。如果item在 self中应该返回真,否则返回假。对于映射对象,它应该考虑映射的键而不是值或者键值对。
对于没有定义__contains__()的对象,成员测试首先通过__iter__()尝试迭代,然后通过__getitem__()尝试旧式的序列迭代协议, 参见该语言参考中的这一节。
3.4.7. 模拟序列类型的额外方法¶
可以定义下面可选的方法以进一步模拟序列对象。不可变序列的方法应该至多值定义__getslice__();可变对象可以定义所有的三种方法。
- object.__getslice__(self, i, j)¶
-
从2.0版后废弃: 支持类似__getitem__()方法的切片对象。(然后,CPython中的内建类型仍然实现了__getslice__()。因此,当实现切片时你必须在子类中覆盖它。)
调用它以实现计算self[i:j]。返回的对象应该与self的类型相同。注意在切片表达式中省略的i或j将分别被零或sys.maxint替换。如果在切片中使用了负索引,将在该索引加上序列的长度。如果该实例没有实现__len__()方法,将引发一个AttributeError。无法保证这种方法调整过的索引仍然不是负数。大于序列长度的索引不会给修改。如果找不到__getslice__(),则创建一个切片对象并传递给__getitem__()。
- object.__setslice__(self, i, j, sequence)¶
-
调用它以实现对self[i:j]的赋值。i和j的注意事项与__getslice__()相同。
该方法被废弃了。如果找不到__setslice__(),或者对于扩展的切片形式self[i:j:k],则创建一个切片对象并传递给__setitem__(),而不是调用__setslice__()。
- object.__delslice__(self, i, j)¶
-
调用它以实现对self[i:j]的删除。i和j的注意事项与__getslice__()相同。该方法已经被废弃了。如果找不到__delslice__(),或者对于扩展的切片形式self[i:j:k],则创建一个切片对象并传递给__delitem__(),而不是调用__delslice__()。
注意这些方法只有在使用具有单个冒号的单个切片且切片方法可用时才调用。对于调用扩展切片符号的切片操作,或者切片方法不存在时,则以一个切片对象作为参数调用__getitem__(), __setitem__()或__delitem__()。
下面的实例演示如何使得你的程序或模块与早期版本的Python兼容(假设__getitem__(), __setitem__()和__delitem__()方法支持切片对象为参数):
class MyClass:
...
def __getitem__(self, index):
...
def __setitem__(self, index, value):
...
def __delitem__(self, index):
...
if sys.version_info < (2, 0):
# They won't be defined if version is at least 2.0 final
def __getslice__(self, i, j):
return self[max(0, i):max(0, j):]
def __setslice__(self, i, j, seq):
self[max(0, i):max(0, j):] = seq
def __delslice__(self, i, j):
del self[max(0, i):max(0, j):]
...
注意对max()的调用;它们是必要的因为在调用__*slice__()方法前对负索引的处理。当使用负索引时,__*item__()方法收到的参数与提供的参数相同,但是__*slice__()方法得到的是索引值经过“转换后”的形式。对于每一个负索引值,在调用该方法前会加上序列的长度(结果可能仍然是负索引);这是内建序列类型对负索引通常的处理方式,期望__*item__()方法也这么做。然而,因为它们已经这么做了,负索引不可能传进去;它们必须被限制在序列的边界在传递给__*item__()方法之前。顺便调用max(0, i)可以返回合适的值。
3.4.8. 模拟数值类型¶
可以定义下面的方法来模拟数值对象。实现的特殊数值类型如果不支持某种操作,对应的方法应该保持未定义(例如,非整数数值的位操作)。
- object.__add__(self, other)¶
- object.__sub__(self, other)¶
- object.__mul__(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.__div__(self, other)¶
- object.__truediv__(self, other)¶
-
除法操作(/)通过这些方法实现。__truediv__()方法在__future__.division生效时使用,否则使用__div__()。只有这两个方法只定义了一个时,对象才不会支持可选上下文中的除法;取而代之的是引发TypeError。
- object.__radd__(self, other)¶
- object.__rsub__(self, other)¶
- object.__rmul__(self, other)¶
- object.__rdiv__(self, other)¶
- object.__rtruediv__(self, other)¶
- object.__rfloordiv__(self, other)¶
- object.__rmod__(self, other)¶
- object.__rdivmod__(self, other)¶
- object.__rpow__(self, other)¶
- object.__rlshift__(self, other)¶
- object.__rrshift__(self, other)¶
- object.__rand__(self, other)¶
- object.__rxor__(self, other)¶
- object.__ror__(self, other)¶
-
调用这些方法以实现具有映射(交换)操作数的二元算术操作(+, -, *, /, %, divmod(), pow(), **, <<, >>, &, ^, |)。这些函数只有当左操作数不支持对应的操作且该操作数是不同的类型。[2]例如,要计算表达式x - y,其中y是一个具有__rsub__()方法的类的实例,如果x.__sub__(y)返回NotImplemented则调用y.__rsub__(x)。
注意三个参数的pow()将不会尝试调用__rpow__()(强制转换规则将变得非常复杂)。
Note
如果右操作数类型是左操作数的一个子类且该子类提供相应操作的映射方法,则在调用左操作数的非映射方法前将调用该方法。这种行为允许子类覆盖它们祖先的操作。
- object.__iadd__(self, other)¶
- object.__isub__(self, other)¶
- object.__imul__(self, other)¶
- object.__idiv__(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 += y,其中x是一个具有__iadd__()方法的类的实例,将调用x.__iadd__(y)。如果x是一个不支持__iadd__()方法的类的实例,那么将考虑x.__add__(y)和y.__radd__(x),类似计算x + y。
- object.__neg__(self)¶
- object.__pos__(self)¶
- object.__abs__(self)¶
- object.__invert__(self)¶
-
调用以实现一元算术操作(-, +, abs()和~)。
- object.__index__(self)¶
-
调用以实现operator.index()。也会在Python需要一个整数对象(例如切片)时调用。必须返回一个整数(整数或长整数)。
2.5版中新增。
- object.__coerce__(self, other)¶
-
调用时实现“混合模式“的数值算术。应该返回一个二元元组,包含self和另外一个转换成相同类型的other,如果转换不可能则应该返回None。如果共同的类型是other的类型,返回None就足够了,因为解释器还会请求另外一个对象尝试强制转换(但有时,如果另外一个类型不可以改变,在这里做转换还是有用的)。NotImplemented返回值等同于返回None。
3.4.9. 隐式转换规则¶
这一节用于记录隐式转换的规则。随着语言的逐步发展,隐式转换的规则已经变得很难精确地记录下来;记录某个特殊实现的一个版本所做的工作是不需要的。取而代之,这里是一些针对强制转换的非正式的指导。在Python 3中,隐式转换将不再支持。
-
如果%操作的左操作数是一个字符串或Unicode对象,则不会发生强制转换而是调用字符串格式化操作。
-
不再建议定义强制转换操作。在没有定义强制转换类型上的混合模式操作将传递原始的参数给该操作。
-
New-style classes (those derived from object) never invoke the __coerce__() method in response to a binary operator; the only time __coerce__() is invoked is when the built-in function coerce() is called.
-
对于大多数意图和目的,返回NotImplemented的操作当作与完全没有实现该操作相同。
-
Below, __op__() and __rop__() are used to signify the generic method names corresponding to an operator; __iop__() is used for the corresponding in-place operator. For example, for the operator ‘+‘, __add__() and __radd__() are used for the left and right variant of the binary operator, and __iadd__() for the in-place variant.
-
For objects x and y, first x.__op__(y) is tried. If this is not implemented or returns NotImplemented, y.__rop__(x) is tried. If this is also not implemented or returns NotImplemented, a TypeError exception is raised. 但请参见下面的例外:
-
Exception to the previous item: if the left operand is an instance of a built-in type or a new-style class, and the right operand is an instance of a proper subclass of that type or class and overrides the base’s __rop__() method, the right operand’s __rop__() method is tried before the left operand’s __op__() method.
This is done so that a subclass can completely override binary operators. Otherwise, the left operand’s __op__() method would always accept the right operand: when an instance of a given class is expected, an instance of a subclass of that class is always acceptable.
-
When either operand type defines a coercion, this coercion is called before that type’s __op__() or __rop__() method is called, but no sooner. If the coercion returns an object of a different type for the operand whose coercion is invoked, part of the process is redone using the new object.
-
When an in-place operator (like ‘+=‘) is used, if the left operand implements __iop__(), it is invoked without any coercion. When the operation falls back to __op__() and/or __rop__(), the normal coercion rules apply.
-
在x + y中,如果x是一个实现了序列连接的序列,则调用序列连接。
-
Rich comparisons (implemented by methods __eq__() and so on) never use coercion. Three-way comparison (implemented by __cmp__()) does use coercion under the same conditions as other binary operations use it.
-
在当前实现中,内建数值类型int, long, float和complex不使用强制转换。所有这些类型实现了一个__coerce__()方法,以用于内建的coerce()函数。
2.7版中新的变化。
3.4.10. With语句的上下文管理器¶
2.5版中新引入。
上下文管理器是一个对象,它定义执行with语句时将要建立的运行时上下文。上下文管理器处理代码块执行的入口、出口和需要的运行时上下文。上下文管理器通常使用with语句调用(在with语句一节描述),但也可以通过直接调用它们的方法使用。
上下文管理器的典型用法包括保存和恢复各种全局状态,加锁和解锁资源,关闭打开的文件等等。
关于上下文管理器的更多信息,请参见 Context Manager Types。
- object.__exit__(self, exc_type, exc_value, traceback)¶
-
退出与该对象相关的运行时刻上下文。参数描述导致该上下文即将退出的异常。如果该上下文退出时没有异常,三个参数都将为None。
如果提供了一个异常,但该方法期望压制该异常(例如,防止它扩散),它应该返回一个真值。否则,该异常将在从该函数退出时被正常处理。
注意__exit__()方法不应该重新抛出传递进去的异常;这是调用者的责任。
3.4.11. 旧式类特殊方法的查找¶
对于旧式类,特殊方法的查找始终与其它方法和属性的查找方式完全相同。无论方法的查找是显式的x.__getitem__(i)还是隐式的x[i]。
该行为意味着特殊的方法对于同一个旧式类的不同实例展现不同的行为,如果特殊的属性设置不同:
>>> class C:
... pass
...
>>> c1 = C()
>>> c2 = C()
>>> c1.__len__ = lambda: 5
>>> c2.__len__ = lambda: 9
>>> len(c1)
5
>>> len(c2)
9
3.4.12. 新式类特殊的方法查找¶
对于新式类,特殊方法的隐式调用只有它在对象的类型上定义而不是对象实例的字典中定义时才保证正确地工作。该行为是为什么下面的代码引起一个异常的原因(与旧式类的相同例子不同):
>>> class C(object):
... 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
Bypassing the __getattribute__() machinery in this fashion provides significant scope for speed optimisations within the interpreter, at the cost of some flexibility in the handling of special methods (the special method must be set on the class object itself in order to be consistently invoked by the interpreter).
脚注
[1] | 在某些情况下确实可以改变一个对象的类型,在可控特定的条件下。然而这通常不是一个好主意,因为如果处理不正确它可能导致某些非常奇怪的行为。 |
[2] | 对于类型相同的操作数,Python假设如果非反射方法(例如__add__())失败则不支持该操作,这是为什么不调用反射函数的原因。 |