有时我们希望自定义类的实例之间可以使用逻辑运算符进行比较,我们自定义比较的行为。例如,有一个矩形的类,比较两个矩形的实例时,比较的是它们的面积。
class Rectangle: def __init__(self, w, h): self.w = w self.h = h def area(self): return self.w * self.h rect1 = Rectangle(5,3)rect2 = Rectangle(4,4)rect1 > rect2 # => rect1.area() > rect2.area()
要求:自定义类方法实现逻辑运算符比较。
解决方案:
逻辑运算符重载,需要实现以下方法:
__lt__
,__le__
,__gt__
,__ge__
,__eq__
,__ne__
同时,使用标准库functools下的类装饰器total_ording
可以简化此过程。
- 对于逻辑运算符重载:
数字比较
>>> a,b = 5,3>>> a < bFalse>>> a.__lt__(b) #实际上 < 调用的是 __lt__()方法False>>> a >= bTrue>>> a.__ge__(b) #实际上 >= 调用的是 __ge__()方法True
字符串比较
>>> s1,s2 = 'abc','abd'>>> s1 > s2False>>> s1.__gt__(s2) #实际上 > 调用的是 __gt__()方法False>>> ord('c') > ord('d')False
对于字符串的比较,是从左到右依次比较的,通过ord()
函数进行比较。
集合比较
>>> {1, 2, 3} > {4}False>>> {1, 2, 3} < {4}False>>> {1, 2, 3} == {4}False>>> {1, 2, 3} > {1, 2}True>>> {1, 2, 3} > {1, 3}True
实际上对于集合的比较,为True时,>
表示前面的集合包含后面的集合,<
表示后面的集合包含前面的集合,==
说明两集合一样。
这就说明对于不同基本类型的逻辑运算符比较,逻辑运算符重载方法的实现是不一样的。
- 解决方案:
from abc import ABCMeta, abstractclassmethodfrom functools import total_orderingimport math @total_orderingclass Shape(metaclass=ABCMeta): #抽象基类 @abstractclassmethod def area(self): pass def __lt__(self, obj): return self.area() < obj.area() def __eq__(self, obj): return self.area() == obj.area()class Rectangle(Shape): def __init__(self, w, h): self.w = w self.h = h def area(self): return self.w * self.h def __str__(self): return 'Rectangle: (%s, %s)' % (self.w, self.h)class Circle(Shape): def __init__(self, radius): self.radius = radius def area(self): return round(self.radius ** 2 * math.pi, 1)rect1 = Rectangle(5,3)rect2 = Rectangle(4,4)c = Circle(5)print(rect1 < c)print(c >= rect2)True #结果True
首先定义抽象基类,在基类中自定义逻辑运算符重载方法,通过@total_ordering
装饰器自动补全其余的重载方法,需要进行实例之间逻辑比较的类继承抽象基类即可。