3.3.1. 基本定制

  • object.new(cls[, ])
  • 调用以创建一个 cls 类的新实例。new() 是一个静态方法 (因为是特例所以你不需要显式地声明),它会将所请求实例所属的类作为第一个参数。其余的参数会被传递给对象构造器表达式 (对类的调用)。new() 的返回值应为新对象实例 (通常是 cls 的实例)。

典型的实现会附带适宜的参数使用 super().new(cls[, …]),通过超类的 new() 方法来创建一个类的新实例,然后根据需要修改新创建的实例再将其返回。

如果 new() 返回一个 cls 的实例,则新实例的 init() 方法会在之后被执行,例如 init(self[, …]),其中 self 为新实例,其余的参数与被传递给 new() 的相同。

如果 new() 未返回一个 cls 的实例,则新实例的 init() 方法就不会被执行。

new() 的目的主要是允许不可变类型的子类 (例如 int, str 或 tuple) 定制实例创建过程。它也常会在自定义元类中被重载以便定制类创建过程。

  • object.init(self[, ])
  • 在实例 (通过 new()) 被创建之后,返回调用者之前调用。其参数与传递给类构造器表达式的参数相同。一个基类如果有 init() 方法,则其所派生的类如果也有 init() 方法,就必须显式地调用它以确保实例基类部分的正确初始化;例如: super().init([args…]).

因为对象是由 new()init() 协作构造完成的 (由 new() 创建,并由 init() 定制),所以 init() 返回的值只能是 None,否则会在运行时引发 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 countof an object from going to zero. In this case, the cycle will belater detected and deleted by the cyclic garbage collector. A common cause of reference cycles is whenan exception has been caught in a local variable. The frame'slocals then reference the exception, which references its owntraceback, which references the locals of all frames caught in thetraceback.

参见

gc 模块的文档。

警告

由于调用 del() 方法时周边状况已不确定,在其执行期间发生的异常将被忽略,改为打印一个警告到 sys.stderr。特别地:

  • del() 可在任意代码被执行时启用,包括来自任意线程的代码。如果 del() 需要接受锁或启用其他阻塞资源,可能会发生死锁,例如该资源已被为执行 del() 而中断的代码所获取。

  • del() 可以在解释器关闭阶段被执行。因此,它需要访问的全局变量(包含其他模块)可能已被删除或设为 None。Python 会保证先删除模块中名称以单个下划线打头的全局变量再删除其他全局变量;如果已不存在其他对此类全局变量的引用,这有助于确保导入的模块在 del() 方法被调用时仍然可用。

  • object.repr(self)
  • repr() 内置函数调用以输出一个对象的“官方”字符串表示。如果可能,这应类似一个有效的 Python 表达式,能被用来重建具有相同取值的对象(只要有适当的环境)。如果这不可能,则应返回形式如 <…some useful description…> 的字符串。返回值必须是一个字符串对象。如果一个类定义了 repr() 但未定义 str(),则在需要该类的实例的“非正式”字符串表示时也会使用 repr()

此方法通常被用于调试,因此确保其表示的内容包含丰富信息且无歧义是很重要的。

  • object.str(self)
  • 通过 str(object) 以及内置函数 format()print() 调用以生成一个对象的“非正式”或格式良好的字符串表示。返回值必须为一个 字符串 对象。

此方法与 object.repr() 的不同点在于 str() 并不预期返回一个有效的 Python 表达式:可以使用更方便或更准确的描述信息。

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

  • object.bytes(self)
  • 通过 bytes 调用以生成一个对象的字节串表示。这应该返回一个 bytes 对象。
  • object.format(self, format_spec)
  • 通过 format() 内置函数、扩展、格式化字符串字面值 的求值以及 str.format() 方法调用以生成一个对象的“格式化”字符串表示。 format_spec 参数为包含所需格式选项描述的字符串。 format_spec 参数的解读是由实现 format() 的类型决定的,不过大多数类或是将格式化委托给某个内置类型,或是使用相似的格式化选项语法。

请参看 格式规格迷你语言 了解标准格式化语法的描述。

返回值必须为一个字符串对象。

在 3.4 版更改: object 本身的 format 方法如果被传入任何非空字符,将会引发一个 TypeError

在 3.7 版更改: object.format(x, '') 现在等同于 str(x) 而不再是 format(str(self), '')

  • 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() 以确定结果为真还是假。

在默认情况下 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() 调用以对哈希集的成员进行操作,属于哈希集的类型包括 setfrozenset 以及 dicthash() 应该返回一个整数。对象比较结果相同所需的唯一特征属性是其具有相同的哈希值;建议的做法是把参与比较的对象全部组件的哈希值混在一起,即将它们打包为一个元组并对该元组做哈希运算。例如:
  1. def __hash__(self):
  2. return hash((self.name, self.nick, self.color))

注解

hash() 会从一个对象自定义的 hash() 方法返回值中截断为 Pyssizet 的大小。通常对 64 位构建为 8 字节,对 32 位构建为 4 字节。如果一个对象的 [hash()](https://docs.python.org/zh-cn/3.7/reference/#object.__hash) 必须在不同位大小的构建上进行互操作,请确保检查全部所支持构建的宽度。做到这一点的简单方法是使用 python -c "import sys; print(sys.hash_info.width)"

如果一个类没有定义 eq() 方法,那么也不应该定义 hash() 操作;如果它定义了 eq() 但没有定义 hash(),则其实例将不可被用作可哈希集的项。如果一个类定义了可变对象并实现了 eq() 方法,则不应该实现 hash(),因为可哈希集的实现要求键的哈希集是不可变的(如果对象的哈希值发生改变,它将处于错误的哈希桶中)。

用户定义的类默认带有 eq()hash() 方法;使用它们与任何对象(自己除外)比较必定不相等,并且 x.hash() 会返回一个恰当的值以确保 x == y 同时意味着 x is yhash(x) == hash(y)

一个类如果重载了 eq() 且没有定义 hash() 则会将其 hash() 隐式地设为 None。当一个类的 hash() 方法为 None 时,该类的实例将在一个程序尝试获取其哈希值时正确地引发 TypeError,并会在检测 isinstance(obj, collections.abc.Hashable) 时被正确地识别为不可哈希对象。

如果一个重载了 eq() 的类需要保留来自父类的 hash() 实现,则必须通过设置 hash = <ParentClass>.hash 来显式地告知解释器。

如果一个没有重载 eq() 的类需要去掉哈希支持,则应该在类定义中包含 hash = None。一个自定义了 hash() 以显式地引发 TypeError 的类会被 isinstance(obj, collections.abc.Hashable) 调用错误地识别为可哈希对象。

注解

在默认情况下,str、bytes 和 datetime 对象的 hash() 值会使用一个不可预知的随机值“加盐”。虽然它们会在一个单独 Python 进程中保持不变,它们的哈希值在重复运行的 Python 之间是不可预测的。

这种做法是为了防止以下形式的拒绝服务攻击:通过仔细选择输入来利用字典插入操作在最坏情况下的执行效率即 O(n^2) 复杂度。详情见 http://www.ocert.org/advisories/ocert-2011-003.html

改变哈希值会影响集合的迭代次序。Python 也从不保证这个次序不会被改变(通常它在 32 位和 64 位构建上是不一致的)。

另见 PYTHONHASHSEED.

在 3.3 版更改: 默认启用哈希随机化。

  • object.bool(self)
  • 调用此方法以实现真值检测以及内置的 bool() 操作;应该返回 FalseTrue。如果未定义此方法,则会查找并调用 len() 并在其返回非零值时视对象的逻辑值为真。如果一个类既未定义 len() 也未定义 bool() 则视其所有实例的逻辑值为真。