Python 定制类的特殊方法

jopen 10年前

在定制类的过程中,添加的方法和属性能完成大部分工作。但若想要类表现出一些特殊行为或者能够响应某些内建函数或操作符,那么就需要构建一些特殊方法。这些特殊方法的标识是方法名以双下划线(__)开头与结尾,除了常用的构造器 __init__() 外,还有一些常用的特殊方法。

基础方法:

  • C.__new__(self[,arg1,…])        构造器(带一些可选的参数);通常用在设置不可变数据类型的子类
  • C.__del__(self)        解构器
  • C.__str__(self)        可打印的字符串输出;内建 str() 及 print() 函数
  • C.__repr__(self)        运行时的字符串输出;内建 repr() 函数及 ' ' 操作符
  • C.__call__(self,*args)        用于可调用的实例;可以用来替代闭包的实现
  • C.__nonezero__(self)        为实例定义 False 值;内建 bool() 函数
  • C.__len__(self)        长度;内建 len()

类的值比较:

  • C.__cmp__(self,obj)        对象比较;内建 cmp()
  • C.__lt__(self,obj) & C.__le__(self,obj)        小于或小于等于;内建< & <=
  • C.__gt__(self,obj) & C.__ge__(self,obj)        大于或大于等于;内建 > & >=
  • C.__eq__(self,obj) & C.__ne__(self,obj)        等于或不等于;内建 = & !=

类的属性:

  • C.__getattr__(self,attr)        获取属性;内建 getattr();仅在属性没有找到时调用
  • C.__setattr__(self,attr)        设置属性
  • C.__delattr__(self,attr)        删除属性
  • C.__getattribute__(self,attr)        获取属性;内建 getattr();总是被调用
  • C.__get__(self,attr)        (描述符)获取属性
  • C.__set__(self,attr)        (描述符)设置属性
  • C.__delete__(self,attr)        (描述符)删除属性

数值类型,二进制操作符:

  • C.__*add__(self,obj)        加;+ 操作符
  • C.__*sub__(self,obj)        减;+ 操作符
  • C.__*mul__(self,obj)        乘;* 操作符
  • C.__*dev__(self,obj)        除;/ 操作符
  • C.__*truediv__(self,obj)        真正的除法;/ 操作符
  • C.__*floordiv__(self,obj)        地板除;// 操作符
  • C.__*mod__(self,obj)        取模;% 操作符
  • C.__*divmod__(self,obj)        除和取模;内建 divmod()
  • C.__*pow__(self,obj[,mod])        乘幂;内建 pow() ; ** 操作符
  • C.__*lshift__(self,obj)        左移位;<< 操作符
  • C.__*rshift__(self,obj)        右移位;>> 操作符
  • C.__*and__(self,obj)        按位与;& 操作符
  • C.__*or__(self,obj)        按位或;| 操作符
  • C.__*xor__(self,obj)        按位异或;^ 操作符

数值类型,一元操作符:

  • C.__neg__(self)        一元负
  • C.__pos__(self)        一元正
  • C.__abs__(self)        绝对值;内建 abs()
  • C.__invert__(self)        按位求反;内建 ~ 操作符

数值类型,数值转换:

  • C.__complex__(self, com)        内建 complex()
  • C.__int__(self)        内建 int()
  • C.__float__(self)        内建 float()

数值类型,数值压缩:

  • C.__index__(self)        在有必要时,压缩可选的数值类型为整型(比如用于切片索引时等)

序列类型:

  • C.__len__(self)        序列中的项目数
  • C.__getitem__(self, ind)        获取一个元素
  • C.__setitem__(self, ind,val)        设置一个元素
  • C.__delitem__(self, ind)        删除一个元素
  • C.__getslice__(self, ind1,ind2)        获取切片元素
  • C.__setslice__(self, i1, i2,val)        设置切片元素
  • C.__delslice__(self, ind1,ind2)        删除切片元素
  • C.__contains__(self, val)        含有成员;内建 in 关键字
  • C.__*add__(self,obj)        串联;+ 操作符
  • C.__*mul__(self,obj)        重复;* 操作符
  • C.__iter__(self)        生成迭代器;内建 iter() 函数

映射类型:

  • C.__len__(self)        类中的项目数
  • C.__hash__(self)        散列(hash)函数值
  • C.__getitem__(self,key)        获取某个值
  • C.__setitem__(self,key,val)        设置某个值
  • C.__delitem__(self,key)        删除某个值
  • C.__missing__(self,key)        给定键若不存在,则返回一个默认值

一般常用的特殊方法就是上面这些,某些如 coerce() 这样在 Python3 中被删除或失效的内建函数就没有再列出来。因为 Python 的内建类型已经能够满足日常需求,所以下面的例子就只来实现一个功能吧:虽然 float 类型有 .hex() 方法,但内建的 hex() 函数却不支持 float 类型。所以我们来自定义一个可以被内建的 hex() 调用的浮点类型。为了省事,我们就直接从 float 派生了~

class iFloat(float):      def __index__(self):          return int(self)

运行结果如下:

>>> a = iFloat(1.1)  >>> hex(a)  '0x1'

P.S. 上面这个例子是个伪栗子。因为他实际是通过将浮点数强制转换为整数来满足调用 hex() 函数的条件的。按照官方文档的说法,hex() 函数只接受 int 类型做参数,你在 iFloat 里实现 __hex__() 也没用,这也是我把 __oct__() 和 __hex__() 从上面删除的原因。同时按照官方文档的说法,如果你硬要调用内建 hex() 函数,则必须实现 __index__() 方法来返回一个整数(是的,还是得要整数…)

即,下面这种方法是木有用的:

class iFloat(float):      def __hex__(self):          return self.hex()

仍然会报错,尽管 __hex__(self) 可以返回正常值:

>>> a = iFloat(1.1)  >>> hex(a)  Traceback (most recent call last):    File "<pyshell#31>", line 1, in <module>      hex(a)  TypeError: 'iFloat' object cannot be interpreted as an integer  >>> a.__hex__()  '0x1.199999999999ap+0'

觉得这可能是因为内建 hex() 函数已经不只是调用 __hex__() 这么简单了。