C/C++中的重载运算符机制十分的好用,java虽然不能直接重载运算符,但是可以通过实现comparator接口等方式实现类之间的比较,那么python又是如何重载运算符的呢?
重载运算符如果使用得当,那么API会变得更加好用,而且代码会变得更加容易阅读,但是一旦被滥用(比如把加法运算符重载用作减法),那么就会让阅读代码的人感到困惑不解了,另外还可能导致缺陷和意料之外的性能瓶颈。
python对运算符重载进行了一定的限制,很多限制其实和C++是一样的:
python可以通过重载类中内置的魔法方法来实现运算符的重载,下面通过例子来说明运算符重载的方法:
import math
# 定义坐标上的Point类,拥有x坐标和y坐标这两个属性
class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __str__(self):
return "(%s,%s)" % (self.x, self.y)
# 重载绝对值方法,表示含义为点到原点的距离
def __abs__(self):
return math.sqrt(self.x**2+self.y**2)
# 重载取负运算符
def __neg__(self):
return Point(-self.x, -self.y)
# 重载取正运算符
def __pos__(self):
return Point(self.x, self.y)
# 重载二元运算符,加法
def __add__(self, other):
if not isinstance(other, Point):
raise Exception("传入对象不是Point对象的实例")
return Point(self.x+other.x, self.y+other.y)
# 重载二元运算符,减法
def __sub__(self, other):
if not isinstance(other, Point):
raise TypeError("传入对象不是Point对象的实例")
return Point(self.x-other.x, self.y-other.y)
# 重载比较运算符
def __eq__(self, other):
if not isinstance(other, Point):
raise TypeError("传入对象不是Point对象的实例")
return self.x == other.x and self.y == other.y
def __gt__(self, other):
if not isinstance(other, Point):
raise TypeError("传入对象不是Point对象的实例")
return abs(self) > abs(other)
if __name__ == '__main__':
p1 = Point(-1, 2)
print("取绝对值:",abs(p1))
print("取负:",-p1)
p2 = Point(2,4)
print("加法:",p1+p2)
print("减法:",p1-p2)
print("比较大小:", p1>p2)
上面的结果为:
取绝对值: 2.23606797749979
取负: (1,-2)
加法: (1,6)
减法: (-3,-2)
比较大小: False
可以看到,python每次重载一个运算符,都要重载一个特殊方法,对于比较运算符来说,有__lt__,__le__,__eq__,__ne__,__gt__,__ge__,这么六个,如果都有需要用到这些运算符,自己一一实现是很麻烦的,我们可以使用标准库functools中的total_ordering装饰器来简化重载比较运算符。
使用total_ordering很简单,只要在定义的class类上添加上装饰器@total_ordering,并且在类中定义__eq__()方法和__lt__(),__le__(),__gt__(),__ge__()中的一个,装饰器会帮助我们自动实现其余的,下面来看看例子:
import math
from functools import total_ordering
# 定义坐标上的Point类,拥有x坐标和y坐标这两个属性
@total_ordering
class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __str__(self):
return "(%s,%s)" % (self.x, self.y)
# 重载绝对值方法,表示含义为点到原点的距离
def __abs__(self):
return math.sqrt(self.x**2+self.y**2)
# 重载比较运算符,只要到原点的距离相等,就认为两个类相等
def __eq__(self, other):
if not isinstance(other, Point):
raise TypeError("传入对象不是Point对象的实例")
return abs(self) == abs(other)
def __gt__(self, other):
if not isinstance(other, Point):
raise TypeError("传入对象不是Point对象的实例")
return abs(self) > abs(other)
if __name__ == '__main__':
p1 = Point(-1, 2)
p2 = Point(2,4)
print("比较大小:", p1
因为我们实现和等于操作和大于操作,total_ordering就可以通过这两个来实现剩下的比较运算符,例如,小于等于就可以解释为not self > other or self == other,这样就可以实现所有的比较操作。
所有的运算符相关的方法都可以在官方文档中找到,这里罗列部分:
一元运算符 | __neg__-,__pos__+,__abs__abs() |
比较运算符 | __lt__<,__le__<=,__eq__==,__ne__!=,__gt__>,__ge__>= |
算术运算符 | __add__+,__sub__-,__mul__*,__mod__%,__truediv__/,__floordiv__//,__pow__**,__round__round() |
反向算术运算符 | __radd__,__rsub__,__rmul__,__rmod__,__rtruediv__,__rfloordiv__,__rpow__ |
增量赋值算术运算符 | __iadd__,__isub__,__imul__,__imod__,__itruediv__,__ifloordiv__,__ipow__ |
位运算符 | __invert__~,__lshift__<<,__rshift__>>,__and__&,__or__|,__xor__^ |
反向位运算符 | __rlshift__,__rrshift__,__rand__,__ror__,__rxor__ |
增量赋值位运算符 | __ishift__,__irshift__,__iand__,__ior__,__ixor__ |