• Python基础:20类的定制


            类中有一些可自定义的特殊方法,它们中的一些有预定义的默认行为,而其它一些则没有,留到需要的时候去实现。这些特殊方法是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

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

     

  • 相关阅读:
    js字符串使用占位符拼接
    C#解析json字符串总是多出双引号的原因分析及解决办法
    JS 正则验证字符串中是否含有数字
    不错的MVC文章
    Js 更换html同一父元素下子元素的位置
    团队任务个人博客--20160426
    《构建之法》阅读笔记3
    团队任务个人博客20160425
    团队任务个人博客20160424
    软件工程进度条-第八周
  • 原文地址:https://www.cnblogs.com/gqtcgq/p/7247190.html
Copyright © 2020-2023  润新知