Python 定制类的特殊方法与受权

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

基础方法:shell

  • 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)        等于或不等于;内建 = & !=

类的属性:app

  • 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)        按位求反;内建 ~ 操做符

数值类型,数值转换:code

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

数值类型,数值压缩:orm

  • 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__() 这么简单了。

__getattr__()与受权:

在Python 2.2 之前,标准类型还不能够派生。为解决此问题而常被使用的手段是“包装”,这种方式的行为就和他的名字同样:

class WrappedList():
    def __init__(self,obj):
        self.__data = obj

但这还不够,咱们要实现的主要目标是在须要的时候能够像子类同样自动访问被包装对象的属性或方法。而实现这一功能的关键即是 __getattr__() 特殊方法。这个方法的做用是:当试图访问一个实例的属性时,本地属性会首先被找到,若是本地没有就会去类属性和祖先类属性里找,若是那里也没有的话,就会调用实例的 __getattr__() 方法。所以这里就给咱们提供了一个将属性访问重定向到被包装的类型(伪父类)的机会。其具体的实现方法即是,在 __getattr__() 中调用内建的 getattr() 函数:

class WrappedList():
    def __init__(self,obj):
        self.__data = obj
    def __getattr__(self,attr):
        return getattr(self.__data,attr)
    def __str__(self): 
        self.__atime = time()
        return str(self.__data)
    __repr__ = __str__

运行结果:

>>> a = WrappedList([1,2,3])
>>> a
[1, 2, 3]
>>> a.append(4)
>>> a
[1, 2, 3, 4]

不过这里仍有一个缺陷即是,当你对包装对象进行特殊行为时,例如上面 WrappedList 的切片操做,就会遇到问题。由于这已经超出了访问属性的范畴。所以在标准类型已经能够派生的如今,就不必再去包装他们了,至于其余用途么,也能够先尝试用装饰器去实现。

相关文章
相关标签/搜索