python运算符重载

1. 重载的概念

  重载一般指函数重载。是在一个类里面,函数名字相同,而参数不同。由于python参数没有类型,所以在python中,是没有函数重载的。比如下面这样运行后直接报错。写多个同名的函数,只有最后一个生效。

 

2. 运算符重载

  运算符重载指的是将运算符与类方法关联起来,每个运算符对应一个指定的内置方法。python通过重写这些内置的方法,实现运算符的重载功能。

3. __str__重载

  当我们调用print()函数打印对象时,其实是调用了对象的__str__()函数。

class Vector(object):
   def __init__(self, x=None, y=None):
       self.x=x
       self.y=y
   def __str__(self):
       return f"Vector{self.x,self.y}"

v1=Vector(1,1)
print(v1)

 

 4. 加法运算重载

  加法重载为__add__函数或__iadd__函数,两者有区别。对于不可变对象,比如int变量,元组等,只有__add__函数可以重载。没有__iadd__函数。对于可变对象,如类对象,列表等有__add__函数与__iadd__函数。__add__返回新对象,__iadd__返回旧对象。对于可变对象什么时候调用__add__,又什么时候调用__iadd__看下面例子。

 

class Vector(object):
    def __init__(self, x=None, y=None):
        self.x=x
        self.y=y

    #返回新对象
    def __add__(self, other):
        print("调用了_add__")
        #函数可以根据传入的参数类型,决定行为
        if type(other)==Vector:
            x=self.x+other.x
            y=self.y+other.y
        else:
            x=self.x+other
            y=self.y+other
        return Vector(x,y)

    #返回旧对象
    def __iadd__(self, other):
        print("调用了__iadd__")
        self.x+=other.x
        self.y+=other.y
        return self

    def __str__(self):
        return f"Vector{self.x,self.y}"

v1=Vector(1,1)
v2=Vector(1,2)
v3=v1+v2
print(v3)

v4=Vector(0,0)
print("v4的id=",id(v4))
v4+=Vector(0,1)
print("v4的id=",id(v4))
print(v4)

v5=Vector(1,0)
print("v5的id=",id(v5))
v5=v5+Vector(2,2)
print("v5的id=",id(v5))
print(v5)

 

  可以发现使用v4+=Vector(0,1)v4对象的内存地址没有发生变化,因为调用的是__iadd__。而v5=v5+Vector(2,2)内存地址发生了变化,因为调用了__add__,生成了一个新的对象。再看下面的例子:

 

"""
    对象池
        一种内存优化机制
        python3.1开始:不可变数据,具有对象池
"""
#可变对象包括列表、字典等
list01=[10]
print(id(list01))#1698365395464
list01+=[20]#注意与下面list02=list02+[]的区别
print(id(list01))#1698365395464

list02=[10]
print(id(list02))#1698365395976
list02=list02+[20]
print(id(list02))#1698366665544

#不可变包括 字符串,元组、数值
#不可变 都返回新对象,只能调用__add__
tuple1=(20,)
print(id(tuple1))#1698365629640
tuple1+=(10,20)
print(id(tuple1))#1698368962632

#不可变 a=1 print(id(a))#140721907134736 a=a+2#调用_add__,返回新对象 print(id(a))#140721907134800 a+=2#调用_add__,返回新对象 print(id(a))#140721907134864

 

  不可变对象如元组、字符串、数值加法运算,一定是调用了_add_()返回了新的对象。而可变对象如果使用+=运算,则调用__iadd__(),返回旧对象;否则调用__add__()返回新对象。

5. ==重载

  使用__eq__函数重载==(其实当我们一个对象与另外一个是否相等时也是调用了__eq__函数,比如使用in判断时)

class Vector(object):
    def __init__(self, x=None, y=None):
        self.x=x
        self.y=y

  def __str__(self): return f"Vector({self.x,self.y})" def __eq__(self, other): #默认:按地址比较 #return id(self)==id(other) #重写:比较内容 return self.__dict__==other.__dict__ v1=Vector(1,2) v2=Vector(1,2) print(v1==v2)# print(v1.__eq__(v2))#

  我们希望,如果对象的成员变量值完全相同,认为两者相等,重写__eq__时,使用return self.__dict__==other.__dict__判断。还有下面一种情况:

 1 class Point(object):
 2     def __init__(self, color:str,x=None, y=None):
 3         self.color=color
 4         self.x=x
 5         self.y=y
 6 
 7     def __str__(self):
 8         return f"Vector({self.color,self.x,self.y})"
 9 
10     # 默认:按地址比较
11     # 重写:比较内容
12     def __eq__(self, other):
13         print(type(other))#如果下面return self.color==other这一句,第一次会打印Point类型,第二次打印str类型,自己debug一下就明白了
14         #return self.color==other.color #==两边必须是Point类型对象
15         return self.color==other  #使用==时,如果左边或者右边有Point类型对象,会再次调用__eq__()函数
16 
17 p1=Point('red',1,1)
18 p2=Point('red',1,2)
19 p3=Point('green',1,1)
20 
21 print(p1==p2)#True
22
23 """
24     如果使用第14行 return self.color==other.color,也可以达到print(p1==p2)同样的效果,但是不能处理下面的情况
25 """
26 print(p1=='red')#True 只调用了一次__eq__()
27 #还有下面情况
28 lst_color=[p1,p2,p3]
29 if "red"  in lst_color:#lst_color列表每个元素为Point类型对象
30     print(True)  #两次输出True
31 #所以使用return self.color==other时,不仅可以判断对象是否存在与列表中,又可以判断某个成员变量是否存在于列表中

  再看下面的删除操作(在上面代码后面加上如下代码):

[print(item) for item in lst_color] #运行结果的上面红色框
#再删除第一个有red的Point的对象
if "red" in lst_color:
    lst_color.remove("red")
[print(item) for item in lst_color] #运行结果的下面红色框

  结果如下(通过某个实例成员变量成功删除列表中的某个对象):

6. 大于重载

  使用__gt__函数,往往用于比较两个对象或是对对象进行排序。

class Vector(object):
    def __init__(self, x=None, y=None):
        self.x=x
        self.y=y
def __str__(self):
        return f"Vector({self.x,self.y})"

    def __gt__(self, other)->bool:
        return self.x>other.x

list01=[Vector(1,2),Vector(3,2),Vector(0,1)]
print(max(list01))
print("排序前",end=" ")
for item in list01:
    print(item,end=" ")
print()
list01.sort()#升序
list01.sort(reverse=True)#逆序
print("排序后",end=" ")
for item in list01:
    print(item,end=" ")

 

7. 减法重载

  使用__sub__函数与__isub__函数

class Vector(object):
   def __init__(self, x=None, y=None):
       self.x=x
       self.y=y

def __str__(self): return f"Vector{self.x,self.y}" def __sub__(self, other): x=self.x-other.x y=self.y-other.y return Vector(x,y) def __isub__(self, other): self.x -= other.x self.y -= other.y return self v1=Vector(1,2) print(id(v1)) v1-=Vector(1,1) print(v1) print(id(v1))

 小结:特表要注意__eq__()函数的用法与技巧,本文除了以上介绍的之外,还有很多,可以参考文章最后的链接,讲解的比较详细。

 

参考资料:

https://blog.csdn.net/weixin_42272768/article/details/124443158

https://blog.csdn.net/XQC_KKK/article/details/121889641

  若存在不足或错误之处,欢迎指正与评论!

posted @ 2023-06-14 09:31  wancy  阅读(817)  评论(0编辑  收藏  举报