类中有一些可自定义的特殊方法,它们中的一些有预定义的默认行为,而其它一些则没有,留到需要的时候去实现。这些特殊方法是Python中用来扩充类的强有力的方式。它们可以实现模拟标准类型和重载操作符等。比如__init__()和__del__()就分别充当了构造器和析够器的功能。

这些特殊这些方法都是以双下划线(__)开始及结尾的。下表进行了总结:

基本定制型

C.__init__(self[, arg1, ...])

构造器(带一些可选的参数)

C.__new__(self[, arg1, ...])

构造器(带一些可选的参数);通常用在设置不变数据类型的子类。

C.__del__(self)

解构器

C.__str__(self)

可打印的字符输出;内建str()及print 语句

C.__repr__(self)

运行时的字符串输出;内建repr() 和``操作符

C.__unicode__(self)

Unicode 字符串输出;内建unicode()

C.__call__(self, *args)

表示可调用的实例

C.__nonzero__(self)

为object 定义False 值;内建bool() (从2.2 版开始)

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, val)

设置属性

C.__delattr__(self, attr)

删除属性

C.__getattribute__(self, attr)

获取属性;内建getattr();总是被调用

C.__get__(self, attr)

(描述符)获取属性

C.__set__(self, attr, val)

(描述符)设置属性

C.__delete__(self, attr)

(描述符)删除属性

数值类型:二进制操作符

C.__*add__(self, obj)

加;+操作符

C.__*sub__(self, obj)

减;-操作符

C.__*mul__(self, obj)

乘;*操作符

C.__*div__(self, obj)

除;/操作符

C.__*truediv__(self, obj)

True 除;/操作符

C.__*floordiv__(self, obj)

Floor 除;//操作符

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(复数);内建complex()

C.__int__(self)

转为int;内建int()

C.__long__(self)

转 .long;内建long()

C.__float__(self)

转为float;内建float()

数值类型:基本表示法(String)

C.__oct__(self)

八进制表示;内建oct()

C.__hex__(self)

十六进制表示;内建hex()

数值类型:数值压缩

C.__coerce__(self, num)

压缩成同样的数值类型;内建coerce()

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)

mapping 中的项的数目

C.__hash__(self)

散列(hash)函数值

C.__getitem__(self,key)

得到给定键(key)的值

C.__setitem__(self,key,val)

设置给定键(key)的值

C.__delitem__(self,key)

删除给定键(key)的值

C.__missing__(self,key)

给定键如果不存在字典中,则提供一个默认值

一:简单定制

class  RoundFloatManual(object):
def __init__(self, val):
assert isinstance(val, float), "Value must be a float!"
self.value = round(val, 2) >>> rfm =RoundFloatManual(42)
Traceback (mostrecent call last):
File"<stdin>", line 1, in ?
File"roundFloat2.py", line 5, in __init__
assertisinstance(val, float), \ AssertionError: Value must be a float! >>> rfm =RoundFloatManual(4.2)
>>> rfm
<roundFloat2.RoundFloatManualobject at 0x63030> >>> print rfm
<roundFloat2.RoundFloatManualobject at 0x63030>

它因输入非法而异常,但如果输入正确时,就没有任何输出了。在解释器中,我们得到一些信息,却不是我们想要的。print(使用str())和真正的字符串对象表示(使用repr())都没能显示更多有关我们对象的信息。这就需要实现__str__()和__repr__()二者之一,或者两者都实现。加入下面的方法:

def  __str__(self):
return str(self.value)

现在我们得到下面的:

>>> rfm  =  RoundFloatManual(5.590464)
>>> rfm
<roundFloat2.RoundFloatManual object at 0x5eff0> >>> print rfm
5.59 >>> rfm = RoundFloatManual(5.5964)
>>> print rfm
5.6

但是在解释器中转储(dump)对象时,仍然显示的是默认对象符号,要修复它,只需要覆盖__repr__()。可以让__repr__()和__str__()具有相同的代码,但最好的方案是:__repr__ = __str__

在带参数5.5964的第二个例子中,我们看到它舍入值刚好为5.6,但我们还是想显示带两位小数的数。可以这样修改:

def  __str__(self):
return '%.2f' % self.value

这里就同时具备str()和repr()的输出了:

>>> rfm =RoundFloatManual(5.5964)
>>> rfm
5.60 >>> printrfm
5.60

所有代码如下:

class  RoundFloatManual(object):
def __init__(self,val):
assert isinstance(val, float), "Valuemust be a float!"
self.value = round(val, 2) def __str__(self): return '%.2f' % self.value __repr__ = __str__

二:数值定制

定义一个Time60,其中,将整数的小时和分钟作为输入传给构造器:

class  Time60(object):
def __init__(self, hr, min):
self.hr = hr
self.min = min

1:显示

需要在显示实例的时候,得到一个有意义的输出,那么就要覆盖__str__()(如果有必要的话,__repr__()也要覆盖):

def  __str__(self):
return '%d:%d' % (self.hr, self.min)

比如:

>>> mon =Time60(10, 30)
>>> tue =Time60(11, 15)
>>>
>>> print mon, tue
10:30 11:15

2:加法

Python中的重载操作符很简单。像加号(+),只需要重载__add__()方法,如果合适,还可以用__radd__()及__iadd__()。注意,实现__add__()的时候,必须认识到它返回另一个Time60对象,而不修改原mon或tue:

def  __add__(self,  other):
return self.__class__(self.hr + other.hr, self.min + other.min)

在类中,一般不直接调用类名, 而是使用self 的__class__属性,即实例化self 的那个类,并调用它。 调用self.__class__()与调用Time60()是一回事。但self.__class__()的方式更好。

>>> mon  =  Time60(10, 30)
>>> tue = Time60(11, 15)
>>> mon +tue
<time60.Time60object at 0x62190> >>> print mon + tue
21:45

如果没有定义相对应的特殊方法,但是却使用了该方法对应的运算,则会引起一个TypeError异常:

>>> mon -tue
Traceback (mostrecent call last): File "<stdin>", line 1, in ?
TypeError:unsupported operand type(s) for -: 'Time60' and 'Time60'

3:原位加法

__iadd__(),是用来支持像mon += tue 这样的操作符,并把正确的结果赋给mon。重载一个__i*__()方法的唯一秘密是它必须返回self:

def  __iadd__(self,  other):
self.hr += other.hr
self.min += other.min
return self

下面是结果输出:

>>> mon  = Time60(10,30)
>>> tue = Time60(11,15)
>>> mon
10:30
>>> id(mon)
401872
>>> mon +=tue
>>> id(mon)
401872
>>> mon
21:45

下面是Time60的类的完全定义:

class  Time60(object):
'Time60 - track hours and minutes' def __init__(self,hr, min):
'Time60 constructor - takes hours andminutes'
self.hr = hr
self.min = min def __str__(self):
'Time60 - string representation'
return '%d:%d' % (self.hr, self.min) __repr__ = __str__ def __add__(self, other):
'Time60 - overloading the additionoperator'
return self.__class__(self.hr + other.hr,self.min +other.min) def __iadd__(self,other):
'Time60 - overloading in-place addition'
self.hr += other.hr
self.min += other.min
return self

4:升华

在这个类中,还有很多需要优化和改良的地方。首先看下面的例子:

>>> wed =Time60(12, 5)
>>> wed
12:5 #正确的显示应该是:“12:05” >>> thu =Time60(10, 30)
>>> fri =Time60(8, 45)
>>> thu +fri
18:75 #正确的显示应该是:19:15

可以做出如下修改:

def  __str__(self):
return '%02d:%02d'%(self.hr, self.min) __repr__ = __str__ def __add__(self, othertime):
tmin = self.min + othertime.min
thr = self.hr + othertime.hr return self.__class__(thr + tmin/60, tmin%60) def __iadd__(self, othertime):
self.min += othertime.min
self.hr += othertime.hr
self.hr += self.min/60
self.min %= 60
return self

三:迭代器

迭代器对象本身需要支持以下两种方法,它们组合在一起形成迭代器协议:

iterator.__iter__()      返回迭代器对象本身。

iterator.next()          从容器中返回下一个元素。

实现了__iter__()和next()方法的类就是一个迭代器。自定义迭代器的例子如下:

RandSeq(Random Sequence),传入一个初始序列,__init__()方法执行前述的赋值操作。__iter__()仅返回self,这就是如何将一个对象声明为迭代器的方式,最后,调用next()来得到迭代器中连续的值。这个迭代器唯一的亮点是它没有终点。代码如下:

class  RandSeq(object):
def __init__(self, seq):
self.data= seq def __iter__(self):
return self def next(self):
return choice(self.data)

运行它,将会看到下面的输出:

>>> from  randseq  import  RandSeq
>>> for eachItem in RandSeq(('rock', 'paper', 'scissors')):
... print eachItem
...
scissors
scissors
rock
paper
paper
scissors
......

四:多类型定制

现在创建另一个新类,NumStr,由一个数字-字符对组成,记为n和s,数值类型使用整型(integer)。用[n::s]来表示它,这两个数据元素构成一个整体。NumStr有下面的特征:

初始化: 类应当对数字和字符串进行初始化;如果其中一个(或两)没有初始化,则使用0和空字符串,也就是, n=0 且s=''作为默认。

加法:   定义加法操作符,功能是把数字加起来,把字符连在一起;比如,NumStr1=[n1::s1]且NumStr2=[n2::s2]。则NumStr1+NumStr2 表示[n1+n2::s1+s2],其中,+代表数字相加及字符相连接。

乘法:  类似的, 定义乘法操作符的功能为, 数字相乘,字符累积相连, 也就是,NumStr1*NumStr2=[n1*n::s1*n]。

False 值:当数字的数值为 0 且字符串为空时,也就是当NumStr=[0::'']时,这个实体即有一个false值。

比较:  比较一对NumStr对象,比如,[n1::s1] vs. [n2::s2],有九种不同的组合。对数字和字符串,按照标准的数值和字典顺序的进行比较。

如果obj1< obj2,则cmp(obj1, obj2)的返回值是一个小于0 的整数, 当obj1 > obj2 时,比较的返回值大于0, 当两个对象有相同的值时, 比较的返回值等于0。

我们的类的解决方案是把这些值相加,然后返回结果。为了能够正确的比较对象,我们需要让__cmp__()在 (n1>n2) 且 (s1>s2)时,返回 1,在(n1<n2)且(s1<s2)时,返回-1,而当数值和字符串都一样时,或是两个比较的结果正相反时(即(n1<n2)且(s1>s2),或相反),返回0. 反之亦然。代码如下:

class  NumStr(object):
def __init__(self, num=0, string=''):
self.__num = num
self.__string = string def __str__(self):
return '[%d :: %r]' % (self.__num, self.__string) __repr__ = __str__ def __add__(self, other):
if isinstance(other, NumStr):
return self.__class__(self.__num + other.__num, self.__string + other.__string) else:
raise TypeError, 'Illegal argument type for built-in operation' def __mul__(self, num):
if isinstance(num, int):
return self.__class__(self.__num * num, self.__string * num)
else:
raise TypeError, 'Illegal argument type for built-inoperation' def __nonzero__(self):
return self.__num or len(self.__string) def __norm_cval(self, cmpres):
return cmp(cmpres, 0) def __cmp__(self, other):
return self.__norm_cval(cmp(self.__num, other.__num))+ \
self.__norm_cval(cmp(self.__string,other.__string))

执行一些例子:

>>> a =NumStr(3, 'foo')
>>> b =NumStr(3, 'goo')
>>> c =NumStr(2, 'foo')
>>> d =NumStr()
>>> e =NumStr(string='boo')
>>> f =NumStr(1)
>>> a
[3 :: 'foo']
>>> b
[3 :: 'goo']
>>> c
[2 :: 'foo']
>>> d
[0 :: '']
>>> e
[0 :: 'boo']
>>> f
[1 :: '']
>>> a <b
True
>>> b <c
False
>>> a ==a
True
>>> b * 2
[6 :: 'googoo']
>>> a * 3
[9 :: 'foofoofoo']
>>> b + e
[3 :: 'gooboo']
>>> e + b
[3 :: 'boogoo']
>>> if d: 'not false'
...
>>> if e: 'not false'
...
'not false'
>>>cmp(a, b)
-1
>>>cmp(a, c)
1
>>>cmp(a, a)
0

如果在__str__中使用“%s”,将导致字符串没有引号:

return  '[%d :: %s]' % (self.__num, self.__string)
>>> print a
[3 :: foo]

第二个元素是一个字符串,如果用户看到由引号标记的字符串时,会更加直观。要做到这点,使用“repr()”表示法对代码进行转换,把“%s”替换成“%r”。这相当于调用repr()或者使用单反引号来给出字符串的可求值版本--可求值版本的确要有引号:

>>> print a
[3 :: 'foo']

__norm_cval()不是一个特殊方法。它是一个帮助我们重载__cmp__()的助手函数:唯一的目的就是把cmp()返回的正值转为1,负值转为-1。cmp()基于比较的结果,通常返回任意的正数或负数(或0),但为了我们的目的,需要严格规定返回值为-1,0 和1。

对整数调用cmp()及与 0 比较,结果即是我们所需要的,相当于如下代码片断:

def  __norm_cval(self, cmpres):
if cmpres< 0:
return -1 elif cmpres> 0:
return 1
else:
return 0

两个相似对象的实际比较是比较数字,比较字符串,然后返回这两个比较结果的和。

Python基础:20类的定制的更多相关文章

  1. 二十六. Python基础(26)--类的内置特殊属性和方法

    二十六. Python基础(26)--类的内置特殊属性和方法 ● 知识框架 ● 类的内置方法/魔法方法案例1: 单例设计模式 # 类的魔法方法 # 案例1: 单例设计模式 class Teacher: ...

  2. 二十. Python基础(20)--面向对象的基础

    二十. Python基础(20)--面向对象的基础 1 ● 类/对象/实例化 类:具有相同属性.和方法的一类人/事/物 对象(实例): 具体的某一个人/事/物 实例化: 用类创建对象的过程→类名(参数 ...

  3. python基础——枚举类

    python基础——枚举类 当我们需要定义常量时,一个办法是用大写变量通过整数来定义,例如月份: JAN = 1 FEB = 2 MAR = 3 ... NOV = 11 DEC = 12 好处是简单 ...

  4. Python基础-类的探讨(class)

    Python基础-类的探讨(class) 我们下面的探讨基于Python3,我实际测试使用的是Python3.2,Python3与Python2在类函数的类型上做了改变 1,类定义语法  Python ...

  5. python基础----元类metaclass

    1 引子 class Foo: pass f1=Foo() #f1是通过Foo类实例化的对象 python中一切皆是对象,类本身也是一个对象,当使用关键字class的时候,python解释器在加载cl ...

  6. python基础20 -------python中的异常处理

    一.python程序中的会出现的错误. 1.语法错误:这种错误根本过不了python解释器的语法检测阶段,必须在程序执行之前进行改正. 2.逻辑错误:这种错误虽然过了语法检测阶段但是程序在执行的过程中 ...

  7. python基础(26):类的成员(字段、方法、属性)

    1. 字段 字段:包括普通字段和静态字段,他们在定义和使用中有所区别,而最本质的区别是内存中保存的位置不同. 普通字段属于对象 静态字段属于类 字段的定义和使用: class Province: # ...

  8. 2015/9/29 Python基础(20):类的授权

    类的授权 1.包装包装在Python编程世界中时经常会被提到的一个术语.它是一个通用的名字,意思是对一个已存在的对象进行包装,不管它是数据类型,还是一段代码,可以是对一个已存在的对象,增加新的,删除不 ...

  9. Python基础(十一) 类继承

    类继承: 继承的想法在于,充份利用已有类的功能,在其基础上来扩展来定义新的类. Parent Class(父类) 与 Child Class(子类): 被继承的类称为父类,继承的类称为子类,一个父类, ...

随机推荐

  1. 蛋疼的JavaScript(二)this

    ### 烦人的this JavaScript的表现与之前学的任何传统的语言都不一样,这个就很烦人,这几天把这个坑扫除了 ### this到底指向什么 总的来说,总结下,this其实取决于调用者的上下文 ...

  2. 【洛谷】【USACO】P1118 数字三角形

    题目描述 FJ and his cows enjoy playing a mental game. They write down the numbers from 1 to N (1 <= N ...

  3. [Git高级教程(二)] 远程仓库版本回退方法 - 梧桐那时雨 - CSDN博客

    1 简介 最近在使用git时遇到了远程分支需要版本回滚的情况,于是做了一下研究,写下这篇博客. 2 问题 如果提交了一个错误的版本,怎么回退版本? 如果提交了一个错误的版本到远程分支,怎么回退远程分支 ...

  4. mybatis的第一个程序

    程序结构图: 表结构: 创表sql: CREATE TABLE `users` (   `id` int(11) NOT NULL AUTO_INCREMENT,   `username` varch ...

  5. jquery判断页面网址是否有效

    方法一:(jQuery方法: 适用所有浏览器) HTML页面: <!DOCTYPE html><html><head lang="en"> &l ...

  6. JavaScript异步

    JavaScript异步类型 延迟类型:setTimeout.setInterval.setImmediate 监听事件:监听new Image加载状态.监听script加载状态.监听iframe加载 ...

  7. vue移动端项目

    用vue mint-ui  jquery-weui写了一个移动端demo 技术栈 vue2.0 vue-router axios mint-ui jquery-weui webpack 页面截图 最后 ...

  8. python正则表达式应用 定义一个函数,求字符串中出现的所有整数之和

  9. 洛谷P1316 P1824

    P1316 丢瓶盖 题目描述 陶陶是个贪玩的孩子,他在地上丢了A个瓶盖,为了简化问题,我们可以当作这A个瓶盖丢在一条直线上,现在他想从这些瓶盖里找出B个,使得距离最近的2个距离最大,他想知道,最大可以 ...

  10. php表单和缩略图处理类是什么样呢

    <?php//封装一个表单验证类//中文验证.邮箱验证.电话号码.手机.QQ.身份证.(由字母.数字.下划线组成,不能以数字开头)header('content-type:text/html;c ...