在我们日常的python应用时,经常会自定义类,比如定义一个任务状态、枚举类型,如果想让这些类的实例支持大小比较,例如<、>等,正常情况下需要手动实现比较运算符的方法,不仅繁琐,还容易遗漏写错。而functools.total_ordering就是来解决这个痛点,它是一个装饰器,能自动帮我们不全比较运算符,只需要手动实现其中一个和相等判断方法,就能让自定义类支持完整的比较操作。让我们看看官方定义:使用 total_ordering 装饰器的类,必须满足两个条件:1. 必须定义以下 4 个方法中的任意 1 个:__lt__()(小于)、__le__()(小于等于)、__gt__()(大于)、__ge__()(大于等于);2. 同时,类必须提供 __eq__() 方法(用于判断相等)。
简单的说,写两个方法,装饰器会补全剩下4个。接下来我们铜鼓实例,结合Enum枚举类,用total_ordering实现美剧值的大小比较,我们看看代码:from enum import Enum, auto # 导入枚举相关模块from functools import total_ordering # 导入total_ordering装饰器# 用total_ordering装饰自定义枚举类@total_orderingclass LifeCycle(Enum): # 定义枚举值,auto()会自动分配递增的整数(0、1、2...) configure = auto() # 配置阶段 glob = auto() # 全局处理阶段 load = auto() # 加载阶段 pre_render = auto() # 预渲染阶段 render = auto() # 渲染阶段 post_render = auto() # 后渲染阶段 save = auto() # 保存阶段 # 手动实现__lt__()方法(小于),这是我们选的1个核心比较方法 def __lt__(self, other): try: # 比较两个枚举实例的value值(auto()分配的整数) return self.value < other.value except AttributeError: # 兼容:如果other不是枚举实例,直接用自身value和other比较 return self.value < other # 手动实现__eq__()方法(相等判断),必须提供 def __eq__(self, other): try: # 比较两个枚举实例的value值 return self.value == other.value except AttributeError: # 兼容:如果other不是枚举实例,直接用自身value和other比较 return self.value == otherv
上面的代码中,我们只手动实现了__lt__()和__eq__()两个方法,但是加上@total_ordering装饰器后,python会自动帮我们实现下面的4个方法:__le__():小于等于(基于 __lt__ 和 __eq__ 推导)__gt__():大于(基于 __lt__ 取反推导)__ge__():大于等于(基于 __gt__ 和 __eq__ 推导)__ne__():不等于(基于 __eq__ 取反推导)
代码中使用了try-except捕获AttributeError,为的是兼容两种比较场景:1.两个LifeCycle枚举实例比较;2.枚举实例和整数比较。实际运行效果如下:# 测试两个枚举实例比较print(LifeCycle.configure < LifeCycle.render) # True(0 < 4)print(LifeCycle.save > LifeCycle.pre_render) # True(6 > 3)print(LifeCycle.load == LifeCycle.load) # Trueprint(LifeCycle.glob != LifeCycle.configure) # True# 测试枚举实例和整数比较print(LifeCycle.post_render < 6) # True(5 < 6)print(LifeCycle.save >= 6) # True(6 >= 6)
当然在使用时也要注意一些问题,必须实现__eq__()方法,否则会报错,这是装饰器推到其它比较方法的前提,只需要实现4个比较方法中的任意1个即可,如果手动实现了个多个比较方法,装饰器会优先使用手动实现的这些。