python - 检查两个对象是否可以相互比较,而不依赖于引发的错误
问题描述
通过“可比较”,我的意思是“能够相互执行比较操作>
, <
, >=
, , <=
, ==
, 并且!=
不提高 a TypeError
”。该属性确实适用于许多不同的类:
1 < 2.5 # int and float
2 < decimal.Decimal(4) # int and Decimal
"alice" < "bob" # str and str
(1, 2) < (3, 4) # tuple and tuple
并且它没有:
1 < "2" # int and str
1.5 < "2.5" # float and str
即使它看起来真的应该:
datetime.date(2018, 9, 25) < datetime.datetime(2019, 1, 31) # date and datetime
[1, 2] < (3, 4) # list and tuple
如this similar question所示,您显然可以检查两个未知类型的对象a
,并b
使用传统的python方法“请求宽恕,而不是许可”并使用try
/except
块:
try:
a < b
# do something
except TypeError:
# do something else
但是捕获异常是昂贵的,我希望第二个分支足够频繁地被采用,所以我想在if
/else
语句中捕获它。我该怎么做?
解决方案
由于在实际执行此类操作之前,无法事先知道是否可以对两种特定类型的操作数执行比较操作,因此您可以做的最接近避免必须捕获 a 的预期行为的方法TypeError
是缓存已知的运算符和已经引起TypeError
before的左右操作数类型的组合。您可以通过创建一个具有此类缓存和包装方法的类来执行此操作,这些方法在继续比较之前进行此类验证:
from operator import gt, lt, ge, le
def validate_operation(op):
def wrapper(cls, a, b):
# the signature can also be just (type(a), type(b)) if you don't care about op
signature = op, type(a), type(b)
if signature not in cls.incomparables:
try:
return op(a, b)
except TypeError:
cls.incomparables.add(signature)
else:
print('Exception avoided for {}'.format(signature)) # for debug only
return wrapper
class compare:
incomparables = set()
for op in gt, lt, ge, le:
setattr(compare, op.__name__, classmethod(validate_operation(op)))
以便:
import datetime
print(compare.gt(1, 2.0))
print(compare.gt(1, "a"))
print(compare.gt(2, 'b'))
print(compare.lt(datetime.date(2018, 9, 25), datetime.datetime(2019, 1, 31)))
print(compare.lt(datetime.date(2019, 9, 25), datetime.datetime(2020, 1, 31)))
会输出:
False
None
Exception avoided for (<built-in function gt>, <class 'int'>, <class 'str'>)
None
None
Exception avoided for (<built-in function lt>, <class 'datetime.date'>, <class 'datetime.datetime'>)
None
这样您就可以使用if
语句而不是异常处理程序来验证比较:
result = compare.gt(obj1, obj2)
if result is None:
# handle the fact that we cannot perform the > operation on obj1 and obj2
elsif result:
# obj1 is greater than obj2
else:
# obj1 is not greater than obj2
这里有一些时间统计:
from timeit import timeit
print(timeit('''try:
1 > 1
except TypeError:
pass''', globals=globals()))
print(timeit('''try:
1 > "a"
except TypeError:
pass''', globals=globals()))
print(timeit('compare.gt(1, "a")', globals=globals()))
这在我的机器上输出:
0.047088712933431365
0.7171912713398885
0.46406612257995117
正如您所看到的,缓存的比较验证确实在比较引发异常时为您节省了大约 1/3 的时间,但在没有引发异常时会慢大约 10 倍,因此这种缓存机制只有在您预期巨大的您的大多数比较都会引发异常。
推荐阅读
- node.js - mongodb:集合中的 find() 查询
- uwp - 将字节数组文件写入 OneDrive
- php - 用数据库中的字段替换 % 之间的文本
- mongodb - 为什么添加索引会降低性能?
- vb.net - 在 PictureBox 中显示文件图标
- date - 找不到此日期的格式:“2018-09-09T09:04:47+00:00”
- c++ - 井字游戏:评估节点的启发式价值
- javascript - 如何组织从自己的 React 库中导出的组件
- java - Libgdx 平铺地图不透明
- android - 滚动时CoordinatorLayout内NestedScrollView的波浪效果不起作用