python之运算符重载

最近在学习python高级特性,尤其是关于python类与python设计的运算系统规则。这篇文章总结了一些关于python中运算符重载的设计技巧,在自定义类或者开发自定义库中尤其常见。


第一部分:概览


  •     运算符重载:

        什么是运算符重载
            让自定义的类生成的对象(实例)能够使用运算符进行操作
        作用:
            让自定义的实例像内建对象一样进行运算符操作
            让程序简洁易读
            对自定义对象将运算符赋予新的规则
        算术运算符的重载:
            方法名                  运算符和表达式      说明
            __add__(self,rhs)        self + rhs        加法
            __sub__(self,rhs)        self - rhs         减法
            __mul__(self,rhs)        self * rhs         乘法
            __truediv__(self,rhs)   self / rhs          除法
            __floordiv__(self,rhs)  self //rhs          地板除
            __mod__(self,rhs)       self % rhs       取模(求余)
            __pow__(self,rhs)       self **rhs         幂运算
            
        示例:
            


 
 
  1. class Mynumber:
  2. def __init__(self,v):
  3. self.data = v
  4. def __repr__(self): #消除两边的尖括号
  5. return "Mynumber(%d)"%self.data
  6. def __add__(self,other):
  7. '''此方法用来制定self + other的规则'''
  8. v = self.data + other.data
  9. return Mynumber(v) #用v创建一个新的对象返回给调用者
  10. def __sub__(self,other):
  11. '''此方法用来制定self - other的规则'''
  12. v = self.data - other.data
  13. return Mynumber(v)
  14. n1 = Mynumber( 100)
  15. n2 = Mynumber( 200)
  16. # n3 = n1 + n2
  17. n3 = n1+n2 # n3 = n1.__add__(n2)
  18. print(n3) #Mynumber(300)
  19. n4 = n3 - n2 #等同于n4 = n3.__sub__(n2)
  20. print( "n4 = ",n4)

        rhs(right hand side) 右手边

        说明:
            运算符重载的方法的参数已经有了固定的含义,不建议改变原有的运算符的含义及参数的意义

        二元运算符的重载方法格式:
            def __xx__(self,other):
                语句块

    练习:
        实现两个自定义列表的相加
        class Mylist:
            def __init__(self,iterable=()):
                self.data = list(iterable)

        L1 = MyList([1,2,3])
        L2 = MyList([4,5,6])
        L3 = L1+L2
        print(L3)  #MyList([1,2,3,4,5,6])
        L4 = L2 + L3
        print(L4) #MyList([4,5,6,1,2,3])

        #试想能否实现以下操作
        L5 = L1 * 3
        print(L5)  #MyList([1,2,3,1,2,3,1,2,3])


 
 
  1. class Mylist:
  2. def __init__(self, iterable=()):
  3. self.data = list(iterable)
  4. def __repr__(self):
  5. return 'Mylist(%s)' % self.data
  6. def __add__(self, lst):
  7. return Mylist(self.data + lst.data)
  8. def __mul__(self, rhs):
  9. # rhs为int类型,不能用rhs.data
  10. return Mylist(self.data * rhs)
  11. L1 = Mylist([ 1, 2, 3])
  12. L2 = Mylist([ 4, 5, 6])
  13. L3 = L1 + L2
  14. print(L3) # Mylist([1,2,3,4,5,6])
  15. L4 = L2 + L1
  16. print(L4) # Mylist([4,5,6,1,2,3])
  17. L5 = L1 * 3
  18. print(L5) # Mylist([1,2,3,1,2,3,1,2,3])

    反向运算符的重载
        当运算符的左侧为内建类型时,右侧为自定义类型进行算术匀算符运算时会出现TypeError错误,因为无法修改内建类型的代码          实现运算符重载,此时需要使用反向运算符的重载
             反向算术运算符的重载:
            方法名                  运算符和表达式       说明
            __radd__(self,lhs)       lhs + self       加法
            __rsub__(self,lhs)       lhs - self       减法
            __rmul__(self,lhs)       lhs * self       乘法
            __rtruediv__(self,lhs)   lhs / self       除法
            __rfloordiv__(self,lhs)  lhs // self      地板除
            __rmod__(self,lhs)       lhs % self       取模(求余)
            __rpow__(self,lhs)       lhs ** self      幂运算
            
    示例:
        


 
 
  1. class Mylist:
  2. def __init__(self, iterable=()):
  3. self.data = list(iterable)
  4. def __repr__(self):
  5. return 'Mylist(%s)' % self.data
  6. def __add__(self, lst):
  7. print( '__add__被调用')
  8. return Mylist(self.data + lst.data)
  9. def __mul__(self, rhs):
  10. # rhs为int类型,不能用rhs.data
  11. print( '__mul__被调用')
  12. return Mylist(self.data * rhs)
  13. def __rmul__(self, lhs):
  14. print( "__rmul__被调用")
  15. return Mylist(self.data * lhs)
  16. L1 = Mylist([ 1, 2, 3])
  17. L2 = Mylist([ 4, 5, 6])
  18. L3 = 3 * L1
  19. print(L3)
  20. L1 += L2
  21. print(L1)
  22. L2 *= 3
  23. print(L2)


    复合赋值算术运算符的重载
        以复合赋值算术运算符 x += y为例,此运算符会优先调用x.__iadd__(y)方法,如果没有__iadd__方法时,则会将复合赋值算术运          算拆解为:x = x + y
        然后调用x = x.__add__(y)方法,如果再不存在__add__方法则会触发TypeError类型的错误异常

        复合赋值算术运算符的重载:
        方法名                  运算符和表达式      说明
        __iadd__(self,rhs)       self += rhs        加法
        __isub__(self,rhs)       self -= rhs         减法
        __imul__(self,rhs)       self *= rhs         乘法
        __itruediv__(self,rhs)   self /= rhs        除法
        __ifloordiv__(self,rhs)  self //=rhs        地板除
        __imod__(self,rhs)       self %= rhs     取模(求余)
        __ipow__(self,rhs)       self **=rhs       幂运算


    比较算术运算符的重载
        比较算术运算符的重载:
        方法名                  运算符和表达式      说明
        __lt__(self,rhs)       self < rhs        小于
        __le__(self,rhs)       self <= rhs       小于等于
        __gt__(self,rhs)       self > rhs        大于
        __ge__(self,rhs)       self >= rhs       大于等于
        __eq__(self,rhs)       self == rhs       等于
        __ne__(self,rhs)       self != rhs       不等于
        

    位运算符重载

            方法名              运算符和表达式        说明
        __and__(self,rhs)       self & rhs           位与
        __or__(self,rhs)        self | rhs              位或
        __xor__(self,rhs)       self ^ rhs             位异或
        __lshift__(self,rhs)    self <<rhs            左移
        __rshift__(self,rhs)    self >>rhs            右移

    反向位运算符重载

              方法名            运算符和表达式       说明
        __and__(self,lhs)       lhs & rhs             位与
        __or__(self,lhs)         lhs | rhs               位或
        __xor__(self,lhs)       lhs ^ rhs               位异或
        __lshift__(self,lhs)    lhs <<rhs              左移
        __rshift__(self,lhs)    lhs >>rhs              右移

    复合赋值位相关运算符重载
            方法名              运算符和表达式        说明
        __iand__(self,rhs)       self & rhs          位与
        __ior__(self,rhs)        self | rhs              位或
        __ixor__(self,rhs)       self ^ rhs            位异或
        __ilshift__(self,rhs)    self <<rhs           左移
        __irshift__(self,rhs)    self >>rhs           右移

        

    一元运算符的重载

     方法名              运算符和表达式        说明
     __neg__(self)         - self           负号
     __pos__(self)         + self           正号
     __invert__(self)      ~ self           取反

    语法:
        class 类名:
            def __xxx__(self):
                pass

    示例见:
        


 
 
  1. class Mylist:
  2. def __init__(self, iterable=()):
  3. self.data = list(iterable)
  4. def __repr__(self):
  5. return 'Mylist(%s)' % self.data
  6. def __neg__(self):
  7. g = (-x for x in self.data)
  8. return Mylist(g)
  9. def __pos__(self):
  10. g = (abs(x) for x in self.data)
  11. return Mylist(g)
  12. l1 = Mylist([ 1, -2, 3, -4, 5, -6])
  13. l2 = - l1
  14. print(l2)
  15. l3 = +l1
  16. print(l3)

in/not in 运算符重载
        格式:
            def __contains__(self,e):
                语句
            注: in / not in 返回布尔值 True / False
            当重载了__contains__后,in和not in运算符都可用
            not in 运算符的返回值与 in相反
        示例:       


 
 
  1. class Mylist:
  2. def __init__(self, iterable=()):
  3. self.data = list(iterable)
  4. def __repr__(self):
  5. return 'Mylist(%s)' % self.data
  6. def __contains__(self, e):
  7. return True if e in self.data else False
  8. l1 = Mylist([ 1, 2, 3, 4, -5, 6])
  9. if 2 in l1: # 等同于if l1.__contains__(4)
  10. print( '2在l1内')
  11. else:
  12. print( '2不在l1内')
  13. if -4 not in l1: # 等同于if not l1.__contains__(4)
  14. print( '-4不在l1内')
  15. else:
  16. print( '-4在l1内')


    索引和切片运算符重载方法:
        方法名                  运算符和表达式              说明
        __getitem__(self,i)     x = self(i)          索引/切片取值
        __setitem__(self,i,v)   self[i] = v          索引/切片赋值
        __delitem__(self,i)     del self[i]          del语句删除索引/切片


        作用:
            让自定义的类型的对象能够支持索引和切片操作
        示例: 


 
 
  1. class Mylist:
  2. def __init__(self, iterable=()):
  3. self.__data = list(iterable)
  4. def __repr__(self):
  5. return 'Mylist(%s)' % self.__data
  6. def __getitem__(self, i):
  7. '索引取值,i绑定[]内的元素'
  8. print( 'i的值', i)
  9. return self.__data[i] # 返回data绑定列表中的第i个元素
  10. def __setitem__(self, i, v):
  11. '''此方法可以让自定义的列表支持索引赋值操作'''
  12. print( '__setitem__被调用,i = ', i, 'v = ', v)
  13. self.__data[i] = v
  14. def __delitem__(self, i):
  15. del self.__data[i] # self.__data.pop(i)
  16. return self
  17. if type(i) is int:
  18. print( '用户正在用索引取值')
  19. elif type(i) is slice:
  20. print( '用户正在用切片取值')
  21. print( '切片的起点是:', i.start)
  22. print( '切片的终点是:', i.stop)
  23. print( '切片的步长是:', i.step)
  24. elif type(i) is str:
  25. print( '用户正在用字符串进行索引操作')
  26. # raise KeyError
  27. return self.__data[i] # 返回data绑定的第i个元素
  28. l1 = Mylist([ 1, 2, 3, 4, -5, 6])
  29. print(l1[ 3]) # 4
  30. l1[ 3] = 400
  31. print(l1) # Mylist([1, 2, 3, 400, -5, 6])
  32. del l1[ 3]
  33. print(l1) # Mylist([1, 2, 3, -5, 6])
  34. print(l1[:: 2]) # [1,3,6]


    slice构造函数
        作用:
            用于创建一个slice对象,此对于用于切片操作的传值
        格式:
            slice(start = None,stop = None ,step = None)
        slice对象的实例属性:
            start  切片的起始值,默认为None
            stop   切片的终止值,默认为None
            step   切片的步长,默认为None

 

特性属性@property
    实现其他语言所拥有的getter和setter功能

    作用:
        用来模拟一个属性
        通过@property装饰器,可以对模拟属性的赋值和取值加以控制
    示例:


 
 
  1. class Student:
  2. def __init__(self, s):
  3. self.__score = s
  4. @property
  5. def score(self):
  6. print( 'getter被调用')
  7. return self.__score
  8. @score.setter
  9. def setScore(self, s):
  10. '''此方法用设置值加以限制以保证数据的准确性setter是用来数据的'''
  11. if 0 <= s <= 100:
  12. self.__score = s
  13. def getScore(self, s):
  14. '''getter只是用来获取数据'''
  15. return self.__score
  16. s = Student( 20)
  17. # s.setScore(100)
  18. score = s.score
  19. print( '成绩是:', score)


        


第二部分:常用


构造函数与表达式: __init__, __sub__

常见运算符重载方法

methodoverloadcall
__init__构造函数对象创建: X = Class(args)
__del__析构函数X对象收回
__add__云算法+如果没有_iadd_, X+Y, X+=Y
__or__运算符|如果没有_ior_,X|Y, X|=Y
_repr__, __str__打印,转换print(X),repr(X),str(X)
__call__函数调用X(*args, **kwargs)
__getattr__点号运算X.undefined
__setattr__属性赋值语句X.any=value
__delattr__属性删除del X.any
__getattribute__属性获取X.any
__getitem__索引运算X[key],X[i:j]
__setitem__索引赋值语句X[key],X[i:j]=sequence
__delitem__索引和分片删除del X[key],del X[i:j]
__len__长度len(X),如果没有__bool__,真值测试
__bool__布尔测试bool(X)
__lt__, __gt__,
__le__, __ge__,
__eq__, __ne__
特定的比较X<Y,X>Y,X<=Y,X>=Y,
X==Y,X!=Y
注释:(lt: less than, gt: greater than,
  le: less equal, ge: greater equal,
  eq: equal, ne: not equal
__radd__右侧加法other+X
__iadd__实地(增强的)加法X+=Y(or else __add__)
__iter__, __next__迭代环境I=iter(X), next()
__contains__成员关系测试item in X(任何可迭代)
__index__整数值hex(X), bin(X),  oct(X)
__enter__, __exit__环境管理器with obj as var:
__get__, __set__,
__delete__
描述符属性X.attr, X.attr=value, del X.attr
__new__创建在__init__之前创建对象

索引和分片: __getitem__, __setitem__

如果在类中定义的话,则对于实例的索引运算,会自动调用__getitem__。当实例X出现X[i]这样的索引运算时,Python会自动调用__getitem__方法

拦截分片

索引迭代: __getitem__

迭代器对象: __iter__,  __next__

尽管上一节__getitem__是有效的,但它是迭代退而求其次的方法。Python所有的迭代环境会有优先尝试__iter__的方法,再尝试__getitem__。

从技术角度上讲,迭代环境是通过iter去尝试寻找__iter__方法来实现,而这种方法返回一个迭代器对象。如果已经提供了,python会重复调用迭代器对象的next()方法,直到发生StopIteration异常。如果没有找到__iter__,python会使用__getitem__机制。

__getattr__和__setattr__捕捉属性的的引用

__getattr__拦截属性.运算符

__repr__和__str__会返回字符串表达形式

__radd__处理右侧加法

__call__拦截调用

当实例调用时,使用__call__方法

__del__是析构器

当实例创建时,就会调用__init__构造方法。当实例空间被收回时,析构函数__del__就会自动执行。

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值