Python 定制类的特殊方法与授权
博客专区 > lionets 的博客 > 博客详情
Python 定制类的特殊方法与授权
lionets 发表于4年前
Python 定制类的特殊方法与授权
  • 发表于 4年前
  • 阅读 612
  • 收藏 15
  • 点赞 0
  • 评论 0

腾讯云 十分钟定制你的第一个小程序>>>   

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

__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 的切片操作,就会遇到问题。因为这已经超出了访问属性的范畴。因此在标准类型已经可以派生的现在,就没必要再去包装他们了,至于其他用途么,也可以先尝试用装饰器去实现。

共有 人打赏支持
粉丝 88
博文 91
码字总数 125050
×
lionets
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: