一、概念:什么是bisect?
bisect实质上是python自带的数组二分查找算法:bisect — 数组二分查找算法【官方文档】
因为本质是二分,所以需满足数组是【有序的】
以下是二分的内置算法:
def index(a, x):
'Locate the leftmost value exactly equal to x'
i = bisect_left(a, x)
if i != len(a) and a[i] == x:
return i
raise ValueError
def find_lt(a, x):
'Find rightmost value less than x'
i = bisect_left(a, x)
if i:
return a[i-1]
raise ValueError
def find_le(a, x):
'Find rightmost value less than or equal to x'
i = bisect_right(a, x)
if i:
return a[i-1]
raise ValueError
def find_gt(a, x):
'Find leftmost value greater than x'
i = bisect_right(a, x)
if i != len(a):
return a[i]
raise ValueError
def find_ge(a, x):
'Find leftmost item greater than or equal to x'
i = bisect_left(a, x)
if i != len(a):
return a[i]
raise ValueError
二、基础段落分割案例
官方给出的案例:
bisect() 函数对于数字表查询也是适用的。 这个例子使用 bisect() 根据一组有序的数字划分点来查找考试成绩对应的字母等级: (如) 90 及以上为 ‘A’,80 至 89 为 ‘B’,依此类推:
>>>def grade(score, breakpoints=[60, 70, 80, 90], grades='FDCBA'):
i = bisect(breakpoints, score)
return grades[i]
>>>[grade(score) for score in [33, 99, 77, 70, 89, 90, 100]]
['F', 'A', 'C', 'C', 'B', 'A', 'A']
三、实用案例
问题描述:
每天白班是6:45-18:45夜班是18:45-次日6:45
给出当前时间班次和上一班次的开始时间和结束时间
不采用bisect,使用if和else需要考虑0点到6点的情况,单独增加判断,代码if-else分支较多,代码逻辑比较冗余:
def get_shift_times(turn: int) -> TurnTimeVo:
current_time = datetime.now()
today = current_time.date()
yesterday = today - timedelta(days=1)
tomorrow = today + timedelta(days=1)
# 一天开始的时间
today_start_time = datetime.strptime(Constants.TODAY_START_TIME, "%H:%M:%S").time()
# 白班开始时间
day_start = datetime.strptime(Constants.WORK_SHIFT_START_TIME, "%H:%M:%S").time()
# 白班结束时间
day_end = datetime.strptime(Constants.WORK_SHIFT_END_TIME, "%H:%M:%S").time()
# 时间段分割
today_start = datetime.combine(today, today_start_time)
today_day_start = datetime.combine(today, day_start)
today_day_end = datetime.combine(today, day_end)
yesterday_day_start = datetime.combine(yesterday, day_start)
yesterday_day_end = datetime.combine(yesterday, day_end)
tomorrow_day_start = datetime.combine(tomorrow, day_start)
if turn == TurnShift.now_turn: # 当前班次
if current_time >= today_start and current_time <= today_day_start:
shift_start = yesterday_day_end
shift_end = today_day_start
elif current_time > today_day_start or current_time <= today_day_end:
shift_start = today_day_start
shift_end = today_day_end
else:
shift_start = today_day_end
shift_end = tomorrow_day_start
elif turn == TurnShift.last_turn: # 上一班次
if current_time >= today_start and current_time <= today_day_start:
shift_start = yesterday_day_start
shift_end = yesterday_day_end
elif current_time > today_day_start or current_time <= today_day_end:
shift_start = yesterday_day_end
shift_end = today_day_start
else:
shift_start = today_day_start
shift_end = today_day_end
else:
raise ValueError("Invalid turn value. Please provide 1 or 2.")
vo = TurnTimeVo(start_time=shift_start, end_time=shift_end)
return vo
采用bisect之后逻辑更为清晰:
def get_shift_times(turn: int) -> TurnTimeVo:
current_time = datetime.now()
today = current_time.date()
yesterday = today - timedelta(days=1)
tomorrow = today + timedelta(days=1)
# 白班开始时间
day_start = datetime.strptime(Constants.WORK_SHIFT_START_TIME, "%H:%M:%S").time()
# 白班结束时间
day_end = datetime.strptime(Constants.WORK_SHIFT_END_TIME, "%H:%M:%S").time()
# 时间段分割
today_day_start = datetime.combine(today, day_start)
today_day_end = datetime.combine(today, day_end)
yesterday_day_start = datetime.combine(yesterday, day_start)
yesterday_day_end = datetime.combine(yesterday, day_end)
tomorrow_day_start = datetime.combine(tomorrow, day_start)
brakpoints = [
yesterday_day_start,
yesterday_day_end,
today_day_start,
today_day_end,
tomorrow_day_start,
]
current_interval_index = bisect.bisect(brakpoints, current_time)
if turn == TurnShift.now_turn: # 当前班次
shift_start = brakpoints[current_interval_index - 1]
shift_end = brakpoints[current_interval_index]
elif turn == TurnShift.last_turn: # 上一班次
shift_start = brakpoints[current_interval_index - 2]
shift_end = brakpoints[current_interval_index - 1]
else:
raise ValueError("Invalid turn value. Please provide 1 or 2.")
vo = TurnTimeVo(start_time=shift_start, end_time=shift_end)
return vo