TL;DR 本文将介绍Python bisect模块在某些场景下的妙用,可以高效和优雅的改善原有使用if-else才能解决问题。

难题:查询整数所属的区间

应用开发过程中,经常出现一种情景,需要你查询一个整数落在哪一个范围内,比如根据消费金额确定优惠金额或者打折力度等。具体的例子有:消费满100元优惠10元,消费满200元优惠25元,等等。

常规解决方案及其缺点

通常情况下,都是使用switch/if-elif来解决的,范围比较少的情况,代码还属于比较简洁的,当范围的数量增加,代码就变的相当的不简洁了,正如下面的代码:

discount = None
if value < 100:
	discount = 0
elif value < 200:
	discount = 10
elif values < 300:
	discount = 25
elif values < 400:
	discount = 42
elif values < 500:
	discount = 53
elif values < 600:
	discount = 64
elif values < 700:
	discount = 75
elif values < 800:
	discount = 86
elif values < 900:
	discount = 97
elif values < 1000:
	discount = 108
else:
	discount = 120

基于bisect的方案

bisect介绍

bisect是python的标准模块,是一个关于数组二分查找法的库,里面提供了在这里非常有用的三个函数bisect_left, bisect_right, bisect. 这三个参数都接受一个array和一个数字,返回将数字插入这个array后这个数字的位置(index),但并不真正执行插入操作。比如:

In[0]: import bisect
In[1]: bisect.bisect([1, 3, 5], 2)
Out[1]:
1

表示如果将2插入1 3 5中间,那么插进去之后的index则为返回值(本例,返回值为1),如果出现相同的值,bisect()函数选择将值插在后面也就是原有值的右侧:

In[0]: import bisect
In[1]: bisect.bisect([1, 3, 5], 3)
Out[1]:
2

bisect_left()函数选择将值插在前面也就是原有值的左侧:

In[0]: import bisect
In[1]: bisect.bisect_left([1, 3, 5], 3)
Out[1]:
1

另外bisect_right()函数是bisect()函数的别名,或者反之。

利用bisect查找整数范围

bisect函数是二分查找,既可以用来插入,当然也可以用来检索信息,比如查找值所属的区段/区间。

前面我们提到的那个函数就可以利用bisect做改写:

mapping = {
	0: 0,
	1:	10,
	2: 25,
	3: 42,
	4: 53,
	5: 64,
	6: 75,
	7: 86,
	8: 97,
	9: 108,
	10: 120,
}

i = bisect(range(100, 1001, 100), value)
discount = mapping[i]

这种方案在业务方案多变,查询范围特别多的情况下具备极大的可维护性和性能优势。