实际案例:
有时我们希望自定义的类,实例间可以使用 ,>=,==,!= 符号进行比较,我们自定义比较的行为。例如,有一个矩形的类,我们希望比较两个矩形的实例时,比较的是他们的面积。
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.area() > rect2.area()
rect1 > rect2
解决方案:
比较符号运算符重载,需要实现以下方法:小于__lt__,小于等于__le__,大于__gt__,_大于等于_ge__,等于___eq__,不能于__ne__。
使用标准库的funtools下的类装饰器total_ordering可以简化需要写所有运算符重载的过程。
2、代码演示(1)实现两个实例之间的比较
from functools import total_ordering
@total_ordering
class Rectangle:
# 定义矩形类
def __init__(self, w, h):
self.w = w
self.h = h
def area(self):
return self.w * self.h
# 小于运算符的重载,other为右边对象
def __lt__(self, obj):
print('in __lt__')
return self.area() < obj.area()
def __eq__(self, obj): # 等于
print('in __eq__')
return self.area() == obj.area()
# def __le__(self, obj): # 小于等于,装饰器会默认给创建
# return self < obj or self == obj
#
# def __gt__(self, obj): # 大于,装饰器会默认给创建
# return not (self < obj or self == obj)
r1 = Rectangle(5, 3)
r2 = Rectangle(4, 4)
# 当解释器看到这样一条语句的时候,实际上调用的是r1.__lt__(r2)
# 3*5=15小于4*4=16
print(r1 < r2)
# 小于等于
print(r1 <= r2)
# 为填添加total_ordering装饰器,可以只重载定义等于和小于
# 两个方法就可以使用小于等于,运行时实际只调用了小于的行为,
# 这是因为使用装饰器后默认使用'__lt__ or __eq__ '给我们创建__le__。
print(r1 <= r2)
print(r1 > r2)
print(r1 >= r2)
print(r1 != r2)
'''
两个实例进行比较不总是同一种对象,比如再定义一个Circle类,
通过两个实例对象比较圆和矩形面积。
'''
class Circle(object):
# 定义一个圆
def __init__(self, r):
self.r = r
def area(self):
return self.r ** 2 * 3.14
r1 = Rectangle(5, 3)
c1 = Circle(3)
# 比较矩形和圆面积,r1 <= c1是支持的,如果反过来c1 <= r1也想支持,
# 就需要在Circle类做同样比较运算符的方法重载。
print(r1 <= c1)
print('---')
print(c1 <= r1)
(2)使用抽象接口和公共基类对不同实例对象比较进行升级优化
'''
对每一种图形都写一遍比较运算符方法的重载,这样的方式很麻烦。
我们可以为图形定义一个公共抽象基类shape,并且在shape中实现这些运算符重载的函数。
在额外的定义一个抽象接口area能比较的都要实现这个area,
让所有的图形都继承这个公共基类,这种方式更好。
'''
# _*_ encoding:utf-8 _*_
from functools import total_ordering
# 导入定义抽象基类库
from abc import ABCMeta, abstractmethod
@total_ordering
class Shape(object):
# 图形的抽象基类
@abstractmethod # 抽象接口装饰器
def area(self):
# 子类中都要实现这个接口
pass
# 小于运算符的重载,obj为右边对象
def __lt__(self, obj):
# 比较之前先做类型判断,不能让对象和数字比较
if not isinstance(obj, Shape):
raise TypeError('obj is not Shape')
print('in __lt__')
return self.area() < obj.area()
def __eq__(self, obj): # 等于
if not isinstance(obj, Shape):
raise TypeError('obj is not Shape')
print('in __eq__')
return self.area() == obj.area()
class Rectangle(Shape): # 继承shape
# 定义矩形类
def __init__(self, w, h):
self.w = w
self.h = h
def area(self):
return self.w * self.h
class Circle(Shape): # 继承shape
# 定义一个圆
def __init__(self, r):
self.r = r
def area(self):
return self.r ** 2 * 3.14
'''
两个实例进行比较不总是同一种对象,比如再定义一个Circle类,
通过两个实例对象比较圆和矩形面积。
'''
r1 = Rectangle(5, 3)
c1 = Circle(3)
print(r1 <= c1)
print(r1 > c1)
# 异常验证
# print(r1 > 1)



