Python 3 文档(简体中文) 3.2.2 documentation

Version: 3.2.2

3. 数据模型

3.1. 对象, 值和类型

Python 中的 对象`(:dfn:`Objects) 是对数据的抽象. 所有 Python 程序中的数据都用对象或者对象关系表示 (Python中连代码也是对象, 这与诺依曼的 “存储程序计算机” 模型在某种意义上是一致的).

每个对象都有一个标识, 一种类型和一个值. 一旦建立, 对象的 标识 就不能改变了; 你可以认为它就是对象的内存地址. ‘is‘ 操作符可以比较两个对象的标识; id() 函式会返回对象标识 (目前是用地址实现的) 的一个整数表示. 对象的 类型`(:dfn:`type) 也是不可变的. [1] 对象的类型确定了对象能够支持的操作 (例如, “它有长度吗?”), 同时它也定义了该种对象的取值范围. type() 函式返回对象的类型 (类型本身也是一个对象),

某些对象的 可以改变, 值可以改变的对象称为是 可变的(mutable), 一旦创建完成值就不能改变的对象称为是 不可变的 (immutable)

(The value of an immutable container object that contains a reference to a mutable object can change when the latter’s value is changed; however the container is still considered immutable, because the collection of objects it contains cannot be changed. So, immutability is not strictly the same as having an unchangeable value, it is more subtle.)

(不可变容器对象如果引用了可变对象, 当可变对象改变了时, 它其实也是被修改了. 但它仍被看作是不可变对象, 这是因为它所包含的对象集合是不能变的, 所以不可变对象与值不可变并不完全一样, 这里实在有些微妙)

一个对象的可变性由它的类型决定, 例如数值, 字符串和元组是不可变的, 而字典和列表是可变的.

Objects are never explicitly destroyed; however, when they become unreachable they may be garbage-collected. An implementation is allowed to postpone garbage collection or omit it altogether — it is a matter of implementation quality how garbage collection is implemented, as long as no objects are collected that are still reachable.

对象从来不会被显式的的释放 (destroyed), 但处于不可达状态的对象会被回收掉. 实现可以选择推迟垃圾回收甚至忽略掉这个过程 — 这是实现垃圾回收机制的质量问题, 与语言本身无关. 只要还处于可达状态的对象不被回收就满足 Python 语言的基本要求.

CPython implementation detail: 当前 CPython 实现使用引用计数机制和一个可选的循环垃圾延时检测机制, 只要对象进入不可达状态, 它就会尽量回收对象, 但不能保证回收含有循环引用的垃圾对象. 关于如何控制循环垃圾对象回收的详细情况, 可以参考 gc 模块). 其他实现的行为与之不同, 而且 CPython 以后可能也会改变这个行为.

注意, 使用实现提供的跟踪和调试工具时可能会导致本该回收的对象不被回收. 此外, 语句 ‘try...except‘ 也可能导致此情况.

有些对象包括对 “外部” 资源的引用, 例如文件或窗口. 垃圾回收会释放这些资源是顺其自然的做法, 但因为并不保证垃圾回收一定会发生, 所以这样的对象一般都提供了显式的方法释放这些资源, 通常是用 close() 方法. 高度推荐使用这种方法释放引用了外部资源的对象. ‘try...finally‘ 和 ‘with‘ 语句为执行这种方法提供了方便.

引用了其它对象的对象叫做 容器, 容器的例子有元组, 列表和字典. 引用是容器值的一部分. 大多数情况下, 当我们谈及一个容器的值时, 指的只是值, 而不是被包含对象的标识符. 但是, 当我们谈及容器对象可变性的时候, 指的就是被直接包含的对象的标识了. 因此, 如果一个不可变对象 (如元组) 包含了可变对象, 只要这个可变对象的值变了则容器的值就也改变了.

类型影响了对象的绝大多数行为, 甚至在某种程度上对对象标识也有重要影响: 对于不可变对象, 计算新值的操作符实际返回的可能是, 一个指向已存在的具有相同类型和值的对象的引用. 对于可变对象来说, 这是不允许的. 例如: 在 a = 1; b = 1 之后, ab 可能指向同一个具有 1 值的对象, 具体如何取决于实现. 但 c = []; d =[] 之后, cd 可以保证是两个不同的, 独立的, 新建的空列表 (注意 c = d = [] 是把相同的对象赋给了 cd).

3.2. 标准类型层次

以下是Python内建类型的列表, 扩展模块 (根据不同实现的情况, 可能是 C, Java 或者其他语言写的) 可以定义其它内建类型. 未来版本的 Python 可能会在此类型层次中增加新的类型 (例如: 有理数, 高效存储的整数数组等), 不过这些类型通常是在标准库中定义的.

以下个别类型描述中可能有介绍 “特殊属性” 的段落, 它们是供实现访问的, 不作为一般用途. 这些定义在未来有可能发生改变:

None

这个类型只具有一个值, 并且这种类型也只有一个对象, 这个对象可以通过内建名字 None 访问, 在许多场合里它表示无值, 例如, 没有显式返回值的函式会返回 None. 这个对象的真值为假.

NotImplemented

这个类型只具有一个值, 并且这种类型也只有一个对象. 这个对象可以通过内建名字 NotImplemented 访问. 如果操作数没有对应实现, 数值方法和厚比较 (rich comparison) 方法就会可能返回这个值 (依赖于操作符, 解释器然后会尝试反射操作 (见后), 或者其它后备操作). 它的真值为真.

Ellipsis

这个类型只具有一个值, 并且这种类型也只有一个对象. 这个对象可以通过字面值 ... 或者内建名字 Ellipsis 访问. 它的真值为真.

numbers.Number

它们由数值型字面值产生, 或者是算术运算符和内建算术函式的返回值. 数值型对象是不可变的, 即一旦创建, 其值就不可改变. Python 数值型和数学上的数字关系当然是非常密切的, 但也受到计算机数值表达能力的限制.

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

numbers.Integral

描述了数学上的整数集 (正负数).

有两类整数:

Integers (int)

整数类型. 表示不限范围的数字. 移位和掩码操作符可以认为整数是这样组织的: 负数用二进制补码的一种变体表示, 符号位会扩展至左边无限多位.
Booleans (bool)

如此设计整数表示方法的一个目的是, 使得负数在移位和掩码操作中能够更有意义.

numbers.Real (float)

浮点数. 本类型表示了机器级的双精度浮点数. 硬件的底层体系结构 (和 C, Java 实现) 对你隐藏了浮点数取值范围和溢出处理的复杂细节. Python不支持单精度浮点数. 使用单精度浮点数的原因一般是为了降低CPU负荷和节省内存, 但是这个努力会被 Python 的对象处理代价所抵消, 因此没有必要同时支持两种浮点数, 使 Python 复杂化.

numbers.Complex (complex)

复数. 本类型用一对机器级的双精度浮点数表示复数. 关于浮点数的介绍也适用于复数类型. 复数 z 的实部和虚部可以通过属性 z.realz.imag 获得.

Sequences

本类型描述的是, 以非负数作为元素索引, 由有限元素构成的有序集合. 内建函式 len() 返回序列数据中的元素数. 当序列长度为 n 时, 索引号为 0, 1,..., n -1. 序列 a 中的项 i, 用 a[i] 表示.

序列也支持切片: a[i:j] 表示满足 i <= k < j 的所有项 a[k]. 在作为表达式使用时, 这个切片与原始的序列类型相同, 这隐含着会重新编号索引, 即从零开始.

个别序列还支持有第三个 “步长” 参数的 扩展切片 : a[i:j:k] 选择了所有索引 x: x = i + n*k, n >= 0 并且 i <= x < j.

序列按照可变性可以分为:

Immutable sequences

一旦建立不可变对象的值就不可修改. (如果这个对象引用了其它对象, 这个被引用的对象可以是可变对象, 并且这个对象的值可以变化. 但是, 不可变对象所包括的可变对象集合是不能变的.)

The following types are immutable sequences:

以下是不可变序列类型:

Strings
Tuples
Bytes
Mutable sequences

可变序列可以在创建后改变, 其下标表示和切片表示可以作为赋值语句和 del 语句的目标.

目前, 有两种内建的可变序列对象:

Lists
Byte Arrays

扩展模块 array 提供另一种可变序列类型, 模块 collections 也是如此.

Set types

这个类型描述的是由有限数量的不可变对象构成的无序集合, 对象不能在集合中重复. 它们不能用任何索引作为下标, 但它们可以被迭代, 内建函式 len() 可以计算集合里的元素数. 集合的常用场合是快速测试某元素是否在集合中, 或者是从一个序列中删除重复元素, 或者是做一些数学运算, 比如求集合的交集, 并集, 差和对称差.

集合的元素与字典键一样, 都遵循不可变性对象的规则. 注意, 数值类型遵守数值比较的正常规则. 即比较相等的两个数值型对象, 只有一个能存在于集合中, 例如, 11.0.

当前有两种内建的集合类型:

Sets

集合. 这表示可变集合, 可以用内建函式 set() 构造, 之后也可以使用用一系列方法修改这个集合, 比如 add().

Frozen sets

这表示一个不可变集合. 由内建函式 frozenset() 构造. 这种类型的对象是不可变的, 并且是可散列的 (hashable), 因此它可以作为另一个集合的元素, 或者作为字典健使用.

Mappings

These represent finite sets of objects indexed by arbitrary index sets. The subscript notation a[k] selects the item indexed by k from the mapping a; this can be used in expressions and as the target of assignments or del statements. The built-in function len() returns the number of items in a mapping.

表示由任意类型作索引的有限对象集合. 下标记法 a[k] 表示在映射类型对象 a 中选择以 k 为索引的项, 这该项可以用于表达式, 作为赋值语句和 del 语句的目标. 内建函式 len() 返回映射对象的元素数量.

目前只有一种内建映射类型:

Dictionaries

表示一个有限对象集合, 几乎可以用任意值索引其中的对象. 包括列表和字典的值可以是值, 但不能是键, 或者其它通过值比较而不是以对象标识比较的可变对象也不能作为键, 其原因是字典的实现效率要求键的散列值保持不变. 数值比较结果相等的两个数值型对象, 例如, 11.0, 在作为字典值的索引 (键) 时是等效的.

字典是可变的, 可以用 {...} 语法创建它们 (参见 Dictionary displays).

扩展模块 dbm.ndbm, dbm.gnucollections 提供了其他映射类型的例子.

Callable types

这是表示功能调用操作的类型 (参见 Calls):

User-defined functions

用户定义函式对象由函式定义 (见 函数定义) 创建. 调用函式时的参数数量, 应该与定义时的形式参数量相同.

特殊属性:

属性 含义  
__doc__ 函式的文档. 字符串, 如果没有 的话就为 None 可写
__name__ 函式名 可写
__module__ 定义函式的模块名, 或者如果没有 对应模块名, 就为 None 可写
__defaults__ 如果任何参数有默认值, 这个分组 保存默认值, 否则为 None 可写
__code__ 表示编译后的函式体的代码对象 可写
__globals__ 函式的全局变量字典引用, 即函式 定义处的全局名字空间. 只读
__dict__ 支持任意函式属性的名字空间. 可写
__closure__ 元组, 含有函式自由变量绑定, 如 果没有自由变量, 就为 None 只读
__annotations__ 一个含有参数注解 (annotations) 的字典, 键为参数名. 如果有返回值, 返回值的键 为 return 可写
__kwdefaults__ 只包括关键字参数默认值的字典 可写

以上大多数标记为 “可写” 的属性都会对赋的值做类型检查.

函式对象也支持用 获得(getting) 和 设置(setting)任意合法 属性(attribute), 比如可以用这种方法将函式与元信息关联起来. 常规的 “点+属性” 就可以获取和设置这些属性. 注意, 当前实现只在用户自定义函式上支持函式属性, 未来版本可能会支持内建函式的函式属性.

函式定义的其它信息可通过它的代码对象获得, 参考下面关于内部类型的介绍.

Instance methods

实例方法对象把类, 类实例和任意可调用对象 (通常是用户定义函式) 组合到了一起.

Special read-only attributes: __self__ is the class instance object, __func__ is the function object; __doc__ is the method’s documentation (same as __func__.__doc__); __name__ is the method name (same as __func__.__name__); __module__ is the name of the module the method was defined in, or None if unavailable.

特殊只读属性: __self__ 是类实例对象, __func__ 是函式对象, __doc__ 是方法的文档 (与 __func__.__doc__ 相同); __name__ 是方法的名字 (与 __func__.__name__ 相同); __module__ 函式定义所在的模块名字, 如果没有对应模块, 就为 None.

方法也支持对底层函式对象任意属性的访问, 但不支持设置.

用户定义方法对象可以通过获取类属性 (也可能是通过该类的一个实例) 创建, 但前提是这个属性是用户定义函式对象, 或者类方法对象.

通过获取一个类实例的用户定义函式, 创建新实例方法对象的时候, 新对象的属性 __self__ 指向该类实例, 这个方法称为是 “被绑定的”. 这个方法的属性 __func__ 指向底层的函式对象.

Modules

模块可以用 import 语句 (见 The import statement) 语句导入. 每个模块都有一个用字典对象实现的名字空间 (在模块中定义的函式的 __global__ 属性引用的就是这个字典). 模块属性的访问被转换成查找这个字典, 例如, m.x 等价于 m.__dict__[" x" ]. 模块对象不包含初始化该模块的代码对象 (因为初始化完成后就不再需要它了).

对模块属性的赋值会更新模块的名字空间, 例如 m.x = 1 等价于 m.__dict__["x"] = 1.

只读特殊属性 __dict__ 就是模块名字空间的字典对象.

预定义的可写属性: __name__ 是模块名; __doc__ 是模块的文档字符串或 None. 如果模块是由文件加载的, __file__ 是对应文件的路径名, 用C语言编写的静态链接进解释器的模块没有这个属性, 而对于从共享库加载的模块, 这个属性的值就是共享库的路径.

Custom classes

定制类类型. 定制类, 一般是由类定义创建的 (见 类定义). 类用字典对象实现其名字空间, 对类属性的访问会转换成对该字典的查找, 例如 C.x 被解释成 C.__dict__[" x" ] (但也有许多钩子机制允许我们用其它方式访问属性). 当此查找没有找到属性时, 搜索会在基类中继续进行. 基类中的搜索方法使用C3方法解析顺序, 这种方法即便是多重继承里出现了公共祖先类的 “菱形” 结构也能保持正确行为. 关于Python使用的 C3 MRO 额外细节可以在 2.3 版本的附带文档中找到:

http://www.python.org/download/releases/2.3/mro/.

当一个类 (假如是类 C) 的属性引用会产生类方法对象时, 它就会被转换成实例方法对象, 并将这个对象的 __self__ 属性指向 C. 当要产生静态方法对象时, 它会被转换成用静态方法对象包装的对象. 另一种获取与 __dict__ 实际内容不同的属性的方法可以参考 实现描述符.

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

一个类对象可以被调用 (如上所述), 以产生一个类实例 (下述).

特殊属性: __name__ 是类名, __module__ 是类定义所在的模块名; __dict__ 是类的名字空间字典. __bases__ 是基类元组 (可能为空或独元), 基类的顺序以定义时基类列表中的排列次序为准. __doc__ 是类的文档字符串或者 None.

Class instances

类实例是用类对象调用创建的. 类实例有一个用字典实现的名字空间, 它是进行属性搜索的第一个地方. 如果属性没在那找到, 但实例的类中有那个名字的属性, 就继续在类属性中查找. 如果找到的是一个用户定义函式对象, 它被转换成实例方法对象, 这个对象的 __self__ 属性指向实例本身. 静态方法和类方法对象也会按上面 “Classes” 中的介绍那样进行转换. 另一种获取与 __dict__ 实际内容不同的属性的方法可以参考 实现描述符. 如果没有找到匹配的类属性, 但对象的类提供了 __getattr__() 方法, 那么最后就会调用它完成属性搜索.

属性的赋值和删除会更新实例字典, 而不是类的字典. 如果类具有方法 __setattr__() 或者 __delattr__() 就会调用它们, 而不是直接更新字典.

如果提供了相应特别方法的定义, 类实例可以伪装成数值, 序列或者映射类型, 参见 特殊方法名.

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

I/O objects (also known as file objects)

文件对象表示已经打开的文件. 创建文件对象有许多不同方法: 内建函式 open(), os.popen(), os.fdopen() 和 socket 对象的 makefile() 方法创建 (其它扩展模块的方法或函式也可以).

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

Internal types

有少量解释器内部使用的类型是用户可见的, 它们的定义可能会在未来版本中改变, 出于完整性的考虑这里也会提一下它们.

Code objects

代码对象表示 字节编译 过的可执行Python代码, 或者称为 bytecode. 代码对象与函式对象的不同在于函式对象包含了函式全局变量的引用 (所在模块定义的), 而代码对象不包括上下文. 默认参数值也保存在函式对象里, 而不在代码对象中 (因为它们表示的是运行时计算出来的值). 不像函式对象, 代码对象是不可变的, 并且不包括对可变对象的 (直接或间接的) 引用.

只读特殊属性: co_name 给出了函式名; co_argcount 是位置参数的数目 (包括有默认值的参数) ; co_nlocals 是函式使用的局部变量的数目 (包括参数). co_varnames 是一个包括局部变量名的元组 (从参数的名字开始) ; co_cellvars 是一个元组, 包括由嵌套函式引用的局部变量名; co_freevals 元组包括了自由变量的名字; co_code 是字节编译后的指令序列的字符串表示; co_consts 元组包括字节码中使用的字面值; co_names 元组包括字节码中使用的名字; co_filename 记录了字节码来自于什么文件; co_firstlineno 是函式首行号; co_lnotab 是一个字符串, 它表示从字节码偏移到行号的映射 (细节可以在解释器代码中找到) ; co_stacksize 是需要的堆栈尺寸 (包括局部变量) ; co_flags 是一个表示解释器各种标志的整数.

co_flags 定义了如下标志位: 如果函式使用了 *arguments 语法接收任意数目的位置参数就会把 0x04 置位; 如果函式使用了 **keywords 语法接收任意数量的关键字参数, 就会把 0x08 置位. 如果函式是一个产生器 (generator), 就会置位 0x20.

“Future功能声明” (from __future__ import division) 也使用了 co_flags 的标志位指出代码对象在编译时是否打开某些特定功能: 如果函式是打开了future division编译的, 就会把 0x2000 置位; 之前版本的Python使用过位 0x100x1000.

co_flags 中其它位由解释器内部保留.

如果代码对象表示的是函式, 那么 co_consts 的第一个项是函式的文档字符串, 或者为 None.

Frame objects

栈桢对象表示执行时的栈桢, 它们会在回溯对象中出现 (下述).

只读特殊属性: 属性 f_back 指向前一个栈桢 (朝着调用者的方向), 如果位于堆栈底部它就是 None; 属性 f_code 指向在这个栈桢结构上执行的代码对象. 属性 f_locals 是用于查找局部变量的字典; 属性 f_globals 字典用于查找全局变量; 属性 f_builtins 字典用于查找内建名字; 属性 lasti 以代码对象里指令字符串的索引的形式给出了精确的指令.

可写特殊属性: 属性 f_trace 如果不是 None, 就是这个栈桢所在函式的名称 (用于调试器). 属性 f_lineno 是此栈帧当前行的行号, 在跟踪函式里如果写入这个属性, 可以使程序跳转到新行上 (只能用于最底部的栈桢), 调试器可以这样实现跳转命令 (即 “指定下一步” 语句).

Traceback objects

回溯对象表示一个 “异常” 的栈回溯. 回溯对象会在发生异常时创建. 当我们在栈桢内搜索异常处理器时, 每当要搜索一个栈桢就会把一个回溯对象会插入到当前回溯对象的前面. 在进行异常处理器时, 回溯对象对程序也就可用了 (参见 try 语句). 这些回溯对象可以通过 sys.exc_info() 返回元组的第三项访问. 当程序中没有适当的异常处理器, 回溯对象就被打印到标准错误输出上. 如果工作在交互模式上, 也可以通过 sys.last_traceback 访问.

只读特殊属性: tb_text 是堆栈回溯的下一级 (向着发生异常的那个栈桢), 或者如果没有下一级就为 None. 属性 tb_frame 指向当前的栈桢对象; 属性 tb_lineno 给出发生异常的行号; 属性 tb_lasti 精确地指出对应的指令. 如果异常发生在没有匹配 exceptfinally 子句的 try 语句中, 回溯对象中的行号和指令可能与栈桢对象中的行号和指令不同.

Slice objects

切片对象用于在 __getitem__() 方法中表示切片信息, 也可以用内建函式 slice() 创建.

只读特殊属性: start 是下界; stop 是上界; step 是步长, 如果忽略任何一个, 就取 None 值. 这些属性可以是任意类型.

切片对象支持一个方法:

slice.indices(self, length)

这个方法根据整数参数 length 判断切片对象是否能够描述 length 长的元素序列. 它返回一个包含三个整数的元组, 分别是索引 start, stop 和步长 step. 对于索引不足或者说越界的情况, 返回值提供的是切片对象中能够提供的最大 (最小) 边界索引.

Static method objects
这种对象提供一种可以绕过上面函式对象到方法对象转换的方法. 静态方法对象一般是其他对象的包装, 通常是用户定义方法. 当从一个类或者类实例获取静态方法对象时, 返回的对象通常是包装过的, 没有经过前面介绍的其他转换. 虽然它所包装的对象经常是可调用的, 但静态方法对象本身是不可调用的. 静态方法对象可以用内建函式 staticmethod() 创建.
Class method objects
类方法对象. 类似于静态方法对象, 也用来包装其他对象的. 是从类或者类实例获取对象的另一种候选方案. 获取对象的具体行为已经在 “用户定义方法” 中介绍过了. 类方法对象可以使用内建函式 classmethod() 创建.

3.3. 特殊方法名

通过定义特殊方法, 类能够实现特殊语法所调用的操作 (例如算术运算, 下标及切片操作). 这是Python方式的运算符重载(operator overloading), 允许类能够针对语言运算符定义自己的行为. 例如, 某个类定义了方法 __getitem__(), 并且 x 是这个类的实例, 那么 x[i] 就粗略等价于 type(x).__getitem__(x, i). 除非特别标示, 在没有适当定义方法的类上执行操作会导致抛出异常, 一般是 AttributeError 或者 TypeError.

在实现要模拟任意内建类型的类时, 需要特别指出的是 “模拟” 只是达到了满足使用的程度, 这点需要特别指出. 例如, 获取某些序列的单个元素是正常的, 但使用切片却是没有意义的 (一个例子是在W3C文档对象模型中的 NodeList 接口.)

3.3.1. 基本定制

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

用于创建类 cls 的新实例. __new__() 是静态方法 (但你并不需要显式地这样声明), 它的第一个参数是新实例的类, 其余的参数就是传递给类构造器 (即类调用) 的那些参数. __new__() 的返回值应该是新对象实例 (一般来说是类 cls 的实例).

这个方法的典型实现是用适当的参数通过 super(currentclass, cls).__new__(cls[,...]) 调用父类的 __new__() 方法创建新实例, 在其基础上做可能的修改, 再返回之.

如果 __new__() 返回了 cls 的一个实例, 之后会以 __init__(self[,...]) 的方式调用新实例的 __init__() 方法, 其中 self 是新实例, 其余参数与传递给 __new__() 的相同.

如果 __new__() 没有返回 cls 的实例, 就不会调用新实例的 __init__().

引入 __new__() 主要是为了允许对不可变类型 (如整数, 字符串和元组) 的子类定制实例. 另外, 它通常也在元类 (metaclass) 定制化时被重载, 目的是定制类的创建.

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

在创建新实例时调用. 参数与传递给类构造表达式的参数相同. 如果基类中定义了 __init__() 方法, 那么必须显式地调用它以确保完成对实例基础部分的初始化. 例如, BaseClass.__init__(self, [args...]). 作为一个构造时的特殊限制, 这个方法不会返回任何值, 否则会导致运行时抛出异常 TypeError.

object.__del__(self)

在实例要被释放 (destroy) 时被调用, 也称为析构器. 如果基类中也有 __del__() 方法, 那么子类应该显式地调用它以确保正确删除实例的基础部分. 注意, 在 __del__() 里可以创建本对象的新引用来达到推迟删除的目的, 但这并不是推荐做法. __del__() 方法在删除最后一个引用后不久调用. 但不能保证, 在解释器退出时所有存活对象的 __del__() 方法都能被调用.

Note

del x 并不直接调用 x.__del__() ——— 前者将引用计数减一, 而后者只有在引用计数减到零时才被调用. 引用计数无法达到零的一些常见情况有: 对象之间的循环引用 (例如, 一个双链表或一个具有父子指针的树状数据结构); 对出现异常的函式的栈桢上对象的引用 (sys.ext_info()[2] 中的回溯对象保证了栈桢不会被删除); 或者交互模式下出现未拦截异常的栈桢上的对象的引用 (sys.last_traceback 中的回溯对象保证了栈桢不会被删除). 第一种情况只有能通过地打破循环才能解决. 后两种情况, 可以通过将 sys.last_traceback 赋予 None 解决. 只有在打开循环检查器选项时 (这是默认的), 循环引用才能被垃圾回收机制发现, 但前提是Python脚本中的 __del__() 方法不要参与进来. 关于 __del__() 与循环检查器是如何相互影响的详细信息, 可以参见 gc 模块的介绍, 尤其是其中的 garbage 值的描述.

Warning

因为调用 __del__() 方法时环境的不确定性, 它执行时产生的异常会被忽略掉, 只是在 sys.stderr 打印警告信息. 另外, 当因为删除模块而调用 __del__() 方法时 (例如, 程序退出时), 有些 __del__() 所引用的全局名字可能已经删除了, 或者正在删除 (例如, 正在清理 import 关系). 由于这些原因, __del__() 方法对外部不变式的要求应该保持最小. 从Python1.5开始, Python 可以保证以单下划线开始的全局名字一定在其它全局名字之前从该模块中删除, 如果没有其它对这种全局名字的引用, 这个功能有助于保证导入的模块在调用 __del__() 时还是有效的.

object.__repr__(self)

使用内建函式 repr() 计算对象的 “正式” 字符串表示时会调用这个方法. 尽可能地, 结果应该是一个能够重建具有相同值的对象的有效 Python 表达式 (在适当环境下). 如果这不可能, 也应该是返回一个形如 <... 一些有用的描述...> 的字符串. 返回值必须是一个字符串对象. 如果类定义了 __repr__() 方法, 但没有定义 __str__(), 那么 __repr__() 也可以用于产生类实例的 “说明性 “字符串描述.

一般来说, 这通常用于调试, 所以描述字符串的信息丰富性和无歧义性是很重要的.

object.__str__(self)

由内建函式 str()print() 调用, 用于计算一个对象的 “说明性” 字符串描述. 与 __repr__() 不同, 这里并不要求一定是有效的 Python 表达式, 可以采用比较通俗简洁的表述方式. 返回值必须是一个字符串对象.

object.__format__(self, format_spec)

由内建函式 format() (和 str 类的方法 format()) 调用, 用来构造对象的 “格式化” 字符串描述. format_spec 参数是描述格式选项的字符串. format_spec 的解释依赖于实现 __format__() 的类型, 但一般来说, 大多数类要么把格式化任务委托 (转交) 给某个内建类型, 或者使用与内建类型类似的格式化选项.

关于标准格式语法的描述, 可以参考 Format Specification Mini-Language.

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

object.__lt__(self, other)
object.__le__(self, other)
object.__eq__(self, other)
object.__ne__(self, other)
object.__gt__(self, other)
object.__ge__(self, other)

它们称为” 厚比较” 方法. 运算符与方法名的对应关系如下: x<y 调用 x.__lt__(y), x<=y 调用 x.__le__(y), x==y 调用 x.__eq__(y), x!=y 调用 x.__ne__(y), x>y 调用 x.__gt__(y), x>=y 调用 x.__ge__(y).

不是所有厚比较方法都要同时实现的, 如果个别厚比较方法没有实现, 可以直接返回 NotImplemented. 从习惯上讲, 一次成功的比较应该返回 FalseTrue. 但是这些方法也可以返回任何值, 所以如果比较运算发生在布尔上下文中 (例如 if 语句中的条件测试), Python会在返回值上调用函式 bool() 确定返回值的真值.

在比较运算符之间并没有潜在的相互关系. x==y 为真并不意味着 x!=y 为假. 因此, 如果定义了方法 __eq__(), 那么也应该定义 __ne__(), 这样才可以得到期望的效果. 关于如何创建可以作为字典键使用的 hashable 对象, 还需要参考 __hash__() 的介绍.

没有参数交换版本的方法定义 (这可以用于当左边参数不支持操作, 但右边参数支持的情况). __lt__()__gt__() 相互反射 (即互为参数交换版本) ; __le__()__ge__() 相互反射; __eq__()__ne__() 相互反射.

传递给厚比较方法的参数不能是被自动强制类型转换的 (coerced).

关于如何从一个根操作自动生成顺序判定操作, 可以参考 functools.total_ordering().

object.__hash__(self)

由内建函式 hash(), 或者是在可散列集合 (hashed collections, 包括 set, frozensetdict) 成员上的操作调用. 这个方法应该返回一个整数. 只有一个要求, 具有相同值的对象应该有相同的散列值. 应该考虑以某种方式 (例如排斥或) 把在对象比较中起作用的部分与散列值关联起来.

如果类没有定义 __eq__() 方法, 那么它也不应该定义 __hash__() 方法; 如果一个类只定义了 __eq__() 方法, 那么它是不适合作散列键的. 如果可变对象实现了 __eq__() 方法, 它也不应该实现 __hash__() 方法, 因为可散列集合要求键值是不可变的 (如果对象的散列值发生了改变, 它会被放在错误的桶 (bucket) 中).

所有用户定义类默认都定义了方法 __eq__()__hash__(), 这样, 所有对象都可以进行相等比较 (除了与自身比较). x.__hash__() 返回 id(x).

如果子类从父类继承了方法 __hash__(), 但修改了 __eq__(), 这时子类继承的散列值就不再正确了 (例如, 可能从默认的标识相等的比较切换成了值相等的比较), 这时在在类定义时显式地将 __hash__() 设置成 None 就行了. 这样, 在使用这个子类对象作为散列键时就会抛出 TypeError 异常, 或者也可以使用常规的可散列检查 isinstance(obj, collections.Hashable) 确定它的不可散列性 (使用这种检查方式时, 这种达到不可散列的方法与在 __hash__() 中显式抛出异常的方法是的计算结果就不一样了).

如果子类修改了 __eq__() 方法, 但需要保留父类的 __hash__(), 它必须显式地告诉解释器 __hash__ = <ParentClass>.__hash__, 否则 __hash__() 的继承会被阻止, 就像设置 __hash__None.

object.__bool__(self)

在实现真值测试和内建操作 bool() 中调用, 应该返回 FalseTrue. 如果没有这个定义方法, 转而使用 __len__(). 非零返回值, 当作 “真”. 如果这两个方法都没有定义, 就认为该实例为 “真”.

3.3.2. 自定义属性权限

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

object.__getattr__(self, name)

在正常方式访问属性无法成功时 (就是说, self属性既不是实例的, 在类树结构中找不到) 使用. name 是属性名. 应该返回一个计算好的属性值, 或抛出一个 AttributeError 异常.

注意, 如果属性可以通过正常方法访问, __getattr__() 是不会被调用的 (是有意将 __getattr__()__setattr__() 设计成不对称的). 这样做的原因是基于效率的考虑, 并且这样也不会让 __getattr__() 干涉正常属性. 注意, 至少对于类实例而言, 不必非要更新实例字典伪装属性 (但可以将它们插入到其它对象中). 需要全面控制属性访问, 可以参考以下 __getattribute__() 的介绍.

object.__getattribute__(self, name)

在访问类实例的属性时无条件调用这个方法. 如果类也定义了方法 __getattr__(), 那么除非 __getattribute__() 显式地调用了它, 或者抛出了 AttributeError 异常, 否则它就不会被调用. 这个方法应该返回一个计算好的属性值, 或者抛出异常 AttributeError. 为了避免无穷递归, 对于任何它需要访问的属性, 这个方法应该调用基类的同名方法, 例如, object.__getattribute__(self, name).

Note

但是, 通过特定语法或者内建函式, 做隐式调用搜索特殊方法时, 这个方法可能会被跳过, 参见 搜索特殊方法.

object.__setattr__(self, name, value)

在属性要被赋值时调用. 这会替代正常机制 (即把值保存在实例字典中). name 是属性名, vaule 是要赋的值.

如果在 __setattr__() 里要对一个实例属性赋值, 它应该调用父类的同名方法, 例如, object.__setattr__(self, name, value).

object.__delattr__(self, name)

__setattr__() 类似, 但它的功能是删除属性. 当 del obj.name 对对象有意义时, 才需要实现它.

object.__dir__(self)

在对象上调用 dir() 时调用, 它需要返回一个列表.

3.3.2.1. 实现描述符

以下方法只能使用在 “描述符类” 中, “描述子类” 的实例出现在其他类 (称为 “所有者类”) 的类字典中, 而这个类包括以下方法. 在下面的例子里, “属性” 专指在所有者类字典中的属性.

object.__get__(self, instance, owner)

在获取所有者类属性或实例属性时调用这个方法. owner 是所有者类, instance 用于访问的所有者类的实例, 如果是通过 owner 访问的话, 这个参数为 None. 这个方法应该返回一个计算好的属性值, 或者抛出异常 AttributeError.

object.__set__(self, instance, value)

在给所有者类的一个实例 instance 设置属性时调用这个方法, value 代表新值.

object.__delete__(self, instance)

删除所有者类实例 instance 的属性时调用这个方法.

3.3.2.2. 调用描述符

一般来说, 描述符就是一个有 “绑定行为” 的对象属性, 这种属性的访问操作会以描述符协议的方式替代, 即方法 __get__(), __set__()__delete__(). 如果一个对象定义了任何以上方法之一, 就称它为 “描述符”.

属性访问的默认行为是从对象字典中获取, 设置, 删除. 例如, a.x 会在导致以下的搜索链: 先 a.__dict__['x']type(a).__dict__['x'], 之后再从 type(a) 的父类中搜索, 但不搜索元类 (metaclass).

但是, 如果搜索的是一个定义了描述符方法的对象, Python 会放弃默认方案转而调用描述符方法. 调用在以上搜索链上的位置取决于定义了什么描述符方法及调用方式的.

描述符调用始于 “绑定”, a.x, 方法参数的组织取决于 a:

Direct Call
直接调用. 这是最简单但也是最不常用的方法, 用户直接调用一个描述符方法, 例如 x.__get__(a).
Instance Binding
实例绑定. 如果与实例绑定, a.x 会转换为以下调用: type(a).__dict__['x'].__get__(a, type(a)).
Class Binding
类绑定. 如果与类绑定, A.x 会转换为以下调用: A.__dict__['x'].__get__(None, A).
Super Binding
超级绑定. 如果 a 是类 super 的一个实例, 那么绑定 super(B, obj).m() 会在 obj.__class__.__mro__ 里直接搜索基类 A, 而不是先搜索 B, 并调用 A.__dict__['m'].__get__(obj, A).

对于实例绑定, 描述符调用的优先顺序依赖于定义了什么描述符方法. 描述符可以定义 __get__(), __set__()__delete__() 的一个任意组合. 如果它没有定义 __get__(), 并且在对象实例的字典中没有这个值, 访问该属性就直接会返回描述符本身. 如果描述符定义了 __set__() 和 (或) __delete__(), 它就是一个数据描述符. 如果两者都没有定义, 它就是非数据描述符. 正常情况下, 数据描述符会定义两个方法 __get__()__set__(), 而非数据描述符只会定义 __get__(). ; 定义了 __set__() and __get__() 数据描述符会覆盖实例的字典, 相比之下, 实例字典会反之覆盖掉非数据描述符.

Python 方法 (包括 staticmethod()classmethod()) 是以非数据描述符实现的. 因此, 实例可以重新定义或者说覆盖方法. 这允许同一个类的不同实例可以有不同的行为.

函式 property() 是用数据描述符实现的, 因此, 实例不能覆写特性 (property) 的行为.

3.3.2.3. __slots__

默认情况下, 类实例使用字典管理属性. 在对象只有少量实例变量时这就会占用不少空间, 当有大量实例时, 空间消耗会变得更为严重.

这个默认行为可以通过在类定义中定义 __slots__ 修改. __slots__ 声明只为该类的所有实例预留刚刚够用的空间. 因为不会为每个实例创建 __dict__, 因此空间节省下来了.

object.__slots__

这个类变量可以赋值为一个字符串, 一个可迭代对象, 或者一个字符串序列 (每个字符串表示实例所用的变量名). 如果定义了 __slots__, Python 就会为实例预留出存储声明变量的空间, 并且不会为每个实例自动创建 __dict____weakref__.

3.3.2.3.1. 使用 __slots__ 的注意事项
  • 如果从一个没有定义 __slots__ 的基类继承, 子类一定存在 __dict__ 属性. 所以, 在这种子类中定义 __slots__ 是没有意义的.

  • 没有定义 __dict__ 的实例不支持对不在 __slot__ 中的属性赋值. 如果需要支持这个功能, 可以把 '__dict__' 放到 __slots__ 声明中.

  • 没有定义 __weakref__ 的, 使用 __slot__ 的实例不支持对它的 “弱引用”. 如果需要支持弱引用, 可以把 '__weakref__' 放到 __slots__ 声明中.

  • __slots__ 是在类这一级实现的, 通过为每个实例创建描述符 (实现描述符). 因此, 不能使用类属性为实例的 __slots__ 中定义的属性设置默认值. 否则, 类属性会覆盖描述符的赋值操作.

  • __slots__ 声明的行为只限于其定义所在的类. 因此, 如果子类没有定义自己的 __slots__ (它必须只包括那些 额外 的 slots), 子类仍然会使用 __dict__.

  • 如果类定义的slot与父类中的相同, 那么父类slot中的变量将成为不可访问的 (除非直接从基类中获取描述符). 这使得程序行为变得有一点模糊, 以后可能会增加一个防止出现这种情况的检查.

    从”变长”内建类型, 例如 int, strtuple 继承的子类的非空 __slots__ 不会起作用.

  • 任何非字符串可迭代对象都可以赋给 __slots__. 映射类型也是允许的, 但是, 以后版本的Python可能给 “键” 赋予特殊意义.

  • 只有在两个类的 __slots__ 相同时, __class__ 赋值才会正常工作.

3.3.3. 类创建的定制

默认情况下, 类是由 type() 构造的. 类的定义会读入一个独立的名字空间, 并且类名称的值会与 type(name, bases, dict) 的返回结果绑定.

在读取类定义时, 如果在基类名之后给出了可调用类型的关键字参数 metaclass (元类), 这时就不再调用 type() 转而使用这个可调用对象. 如果有其它关键字参数, 它们也会传递给 metaclass. 这就允许我们使用类或者函式来监视或修改类创建过程:

  • 在类创建之前修改类的字典.
  • 返回其它类的实例 – 基本上这里执行的就是工厂方法.

以上步骤必须在元类的 __new__() 方法内完成 – 然后从这个方法中调用 type.__new__() 创建具有不同特性 (properties) 的新类. 下面的例子会在创建类之前在类字典中增加一个新元素

class metacls(type):
    def __new__(mcs, name, bases, dict):
        dict['foo'] = 'metacls was here'
        return type.__new__(mcs, name, bases, dict)

你当然也可以覆盖其它类方法 (或是增加新方法), 例如, 在元类中定制 __call__() 方法就可以控制类在调用时的行为, 例如, 不会每次调用都返回一个实例.

如果元类具有 __prepare__() 属性 (一般是用类或者静态方法实现的), 它会在对类定义体 (结合类名, 父类和参数) 估值之前调用. 这个方法应该返回一个支持映射接口的对象, 这个对象用于存储类的名字空间. 默认是一个普通字典. 例如, 可以通过返回一个有序字典跟踪类属性的声明顺序.

使用的元类是按以下优先顺序确定的:

  • 如果在基类位置上使用了 metaclass 关键字参数, 就使用它.
  • 否则, 如果至少有一个基类, 就用基类的元类.
  • 否则, 使用默认元类 type.

元类的用途非常广泛, 目前已知的用法有记录日志, 接口检查, 自动委托, 特性 (property) 自动创建, 代理, 框架, 自动资源锁定及同步.

这里是一个使用 collections.OrderedDict 的元类例子, 它可以记住类成员的定义顺序

class OrderedClass(type):

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

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

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

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

在类定义 A 执行时, 进程开始于调用元类的 __prepare__() 方法并返回一个空 collections.OrderedDict, 这个映射会记录在 class 语句中定义的 A 的方法和属性. 一旦执行完这个定义, 有序字典就完全设置好了, 并调用元类的 __new__() 方法, 这个方法会创建新的类型, 并把这个有序字典的键保存在属性 members 中.

3.3.4. 对实例和子类检查的定制

以下函式可以用于定制内建函式 isinstance()issubclass() 的默认行为.

具体地, 元类 abc.ABCMeta 实现了这些方法, 以允许把抽象基类 (ABC) 作为任何类或者类型 (包括内建类型), 也包括其他ABC的 “虚拟基类”.

class.__instancecheck__(self, instance)
  如果 instance 应该被看作 class 的一个直接或者间接的实例, 就返回真.
如果定义了这个方法, 它就会被用于实现 isinstance(instance, class).
class.__subclasscheck__(self, subclass)

如果 subclass 应该被看作是 class 的一个直接或者间接的基类, 就返回真. 如果定义了这个方法, 它就会被用于实现 issubclass(subclass, class).

注意, 这个方法会在一个类的类型 (元类) 中查找. 它们不能作为一个类方法在类中定义. 这个机制, 与在实例上查找要调用的特殊方法的机制是一致的, 因为在这个情况下实例本身就是一个类.

See also

PEP 3119 - Introducing Abstract Base Classes  (抽象基类介绍)
包括定制通过 __instancecheck__()__subclasscheck__() 定制 isinstance()issubclass() 行为的规范, 以及在语言上增加抽象基类 (见 abc 模块) 这个背景设置这个功能的动机.

3.3.5. 模拟可调用对象

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

当实例作为函式使用时调用本方法. 如果定义了这个方法, x(arg1, arg2,...) 就相当于 x.__call__(arg1, arg2,...).

3.3.6. 模拟容器对象

通过定义以下方法可以实现容器对象. 容器通常指序列 (如列表或元组) 或映射类型 (如字典), 不过也可以表示其它容器. 第一个方法集用于模拟序列或映射: 序列的区别在于, 键只能是整数 k (0 <= k < N, N 是序列的长度) 或者是定义了处在一个范围内的项的切片对象. 另外, 也推荐模拟映射类型时实现方法 keys(), values(), items(), get(), clear(), setdefault(), pop(), popitem(), copy()update(), 这使得模拟出来的行为与 Python 标准字典对象类似. 模块 collections 提供了 MutableMapping 抽象基类可用于帮助从基本方法 __getitem__(), __setitem__(), __delitem__()keys() 中创建这些方法. 可变序列应该提供方法 append(), count(), index(), extend(), insert(), pop(), remove(), reverse()sort(), 就像Python标准列表对象那样. 最后, 序列应该用下述的方法 __add__(), __radd__(), __iadd__(), __mul__(), __rmul__()__imul__() 实现 “加” 操作 (即连接) 和 “乘” 操作 (即重复). 它们也可以实现其它算术运算符. 推荐映射和序列实现 __contains__() 方法以实现 in 操作符的有效使用, 对于映射类型, in 应该搜索映射的键, 对于序列, 它应该搜索值. 进一步的建议是映射和序列实现 __iter__() 方法, 以支持在容器中有效地进行迭代. 对于映射, __iter__() 应该与 keys() 相同, 对于序列, 它应该在值之间迭代.

object.__len__(self)

实现内建函式 len() 相仿的功能. 应该返回对象的长度, 一个大于等于0的整数. 另外, 如果对象没有定义 __bool__() 方法, 那么在布尔上下文中, __len__() 返回的 0 将被看作是 “假”.

Note

切片独立于下面介绍的三个方法, 类似于下面的调用:

a[1:2] = b

会转换为

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

以此类推. 缺少的切片项会被替代为 None.

object.__getitem__(self, key)

用于实现 self[key]. 对于序列, 可接受的 key 应该有整数和切片对象. 注意对负数索引 (如果类希望模拟序列) 的特殊解释也依赖于 __getitem__() 方法. 如果 key 的类型不合适, 可以抛出异常 TypeError. 如果 key 的值在序列的索引范围之外 (在任何负值索引的特殊解释也行不通的情况下), 可以抛出 IndexError 异常. 对于映射类型, 如果没有给出 key (或者不在容器之内), 应该抛出异常 KeyError.

Note

for 循环根据由于无效索引导致的 IndexError 异常检测序列的结尾.

object.__setitem__(self, key, value)

在对 self[key] 赋值时调用. 与 __getitem__() 有着相同的注意事项. 如果映射类型对象要支持改变键的值或者增加新键, 或者序列要支持可替换元素时, 应该实现这个方法. 在使用无效的 key 值时, 会抛出与 __getitem__() 相同的异常.

object.__delitem__(self, key)

在删除 self[key] 时调用, 与 __getitem__() 有相同的注意事项. 如果映射对象要支持删除键, 或者序列对象要支持元素的删除, 就应该实现这个方法. 在使用无效的 key 值时, 会抛出与 __getitem__() 相同的异常.

object.__iter__(self)

在使用容器的迭代器时会调用这个方法. 本方法应该返回一个可以遍历容器内所有对象的迭代器对象. 对于映射类型, 应该在容器的键上迭代, 并且也应该定义 keys() 方法.

迭代器对象也需要实现这个方法, 它们应该返回它自身. 关于迭代器对象的更多信息, 可以参考 Iterator Types.

object.__reversed__(self)

在使用内建函式 reversed() 实现反向迭代时调用这个方法. 它应该返回一个以相反顺序在容器内迭代所有对象的新迭代器对象.

如果没有定义方法 __reversed__(), 内建函式 reversed() 会切换到备用方案: 使用序列协议 (__len__()__getitem__()). 支持序列协议的对象只在有更高效的 reversed() 实现方法时, 才有必要实现 __reversed__().

成员测试运算符 (innot in) 一般是在对序列进行迭代实现的. 但是容器也可以实现方法提供更高效率的实现, 并不要求对象一定是序列.

object.__contains__(self, item)

在成员测试时调用这个方法. 如果 itemself 之内应该返回真, 否则返回假. 对于映射类型的对象, 测试应该是针对键的, 而不是值, 或者键值对.

没有定义 __contains__() 方法的对象, 在进行成员测试时首先尝试使用 __iter__() 方法进行迭代, 然后尝试使用旧式的有序对象迭代协议 __getitem__(), 参见 这里的讨论.

3.3.7. 模拟数值类型

以下方法用于模拟数值类型. 不同数值类型所支持的操作符并不完全相同, 如果一个类型不支持某些操作符 (例如非整数值上的位运算), 对应的方法就不应该被实现.

object.__add__(self, other)
object.__sub__(self, other)
object.__mul__(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.__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.__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.__complex__(self)
object.__int__(self)
object.__float__(self)
object.__round__(self[, n])

用于实现内建函式 complex(), int(), float()round(). 应该返回对应的类型值.

object.__index__(self)

用于实现函式 operator.index(), 或者在Python需要一个整数对象时调用 (例如在分片时 (slicing), 或者在内建函式函式 bin(), hex()oct() 中). 这个方法必须返回一个整数.

3.3.8. With 语句的上下文管理器

上下文管理器 (context manager) 是一个对象, 这个对象定义了执行 with 语句时要建立的运行时上下文. 上下文管理器负责处理执行某代码块时对应的运行时上下文进入和退出. 运行时上下文的使用一般通过 with 语句 (参见 with 语句), 但也可以直接调用它的方法.

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

关于上下文管理的更多信息, 可以参考 Context Manager Types.

object.__enter__(self)

进入与这个对象关联的运行时上下文. with 语句会把这个方法的返回值与 as 子句指定的目标绑定在一起 (如果指定了的话).

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

退出与这个对象相关的运行时上下文. 参数描述了导致上下文退出的异常, 如果是无异常退出, 则这三个参数都为 None.

如果给出了一个异常, 而这个方法决定要压制它 (即防止它把传播出去), 那么它应该返回真. 否则, 在退出这个方法时, 这个异常会按正常方式处理.

注意方法 __exit__() 不应该把传入的异常重新抛出, 这是调用者的责任.

See also

PEP 0343 - The “with” statement (“with” 语句)
Python with 语句的规范, 背景和例子.

3.3.9. 搜索特殊方法

对于定制类, 只有在对象类型的字典里定义好, 才能保证成功调用特殊方法. 这是以下代码发生异常的原因:

>>> 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

这种跳过 __getattribute__() 的机制为解释器的时间性能优化提供了充分的余地, 代价是牺牲了处理特殊方法时的部分灵活性 (为了保持与解释器的一致, 特殊方法必须在类对象中定义).

Footnotes

[1]是在某些受控制的条件下, 修改对象类型是有可能的. 但一般这不是个好做法, 因为一旦处理不周, 就有可能导致一些非常奇怪的行为.
[2]对于相同类型的操作数, 假定如果非反射方法失败就意味着并不支持这个运算符. 这就是没有调用反射方法的原因.