目录
一、应用实例
有时,我们的代码里会出现超过三个分支的
if/else
。就像下面这样:import time def from_now(ts): """接收一个过去的时间戳,返回距离当前时间的相对时间文字描述 """ now = time.time() seconds_delta = int(now - ts) if seconds_delta < 1: return "less than 1 second ago" elif seconds_delta < 60: return "{} seconds ago".format(seconds_delta) elif seconds_delta < 3600: return "{} minutes ago".format(seconds_delta // 60) elif seconds_delta < 3600 * 24: return "{} hours ago".format(seconds_delta // 3600) else: return "{} days ago".format(seconds_delta // (3600 * 24)) now = time.time() print(from_now(now)) print(from_now(now - 24)) print(from_now(now - 600)) print(from_now(now - 7500)) print(from_now(now - 87500)) # OUTPUT: # less than 1 second ago # 24 seconds ago # 10 minutes ago # 2 hours ago # 1 days ago
上面这个函数挑不出太多毛病,很多很多人都会写出类似的代码。但是,如果你仔细观察它,可以在分支代码部分找到一些明显的“边界”。比如,当函数判断某个时间是否应该用“秒数”展示时,用到了
60
。而判断是否应该用分钟时,用到了3600
。从边界提炼规律是优化这段代码的关键。如果我们将所有的这些边界放在一个有序元组中,然后配合二分查找模块 bisect
整个函数的控制流就能被大大简化:
import bisect # BREAKPOINTS 必须是已经排好序的,不然无法进行二分查找 BREAKPOINTS = (1, 60, 3600, 3600 * 24) TMPLS = ( # unit, template (1, "less than 1 second ago"), (1, "{units} seconds ago"), (60, "{units} minutes ago"), (3600, "{units} hours ago"), (3600 * 24, "{units} days ago"), ) def from_now(ts): """接收一个过去的时间戳,返回距离当前时间的相对时间文字描述 """ seconds_delta = int(time.time() - ts) unit, tmpl = TMPLS[bisect.bisect(BREAKPOINTS, seconds_delta)] return tmpl.format(units=seconds_delta // unit)
除了用元组可以优化过多的
if/else
分支外,有些情况下字典也能被用来做同样的事情。关键在于从现有代码找到重复的逻辑与规律,并多多尝试。
二、bisect 学习
import bisect # 等同于 bisect_right # bisect.bisect(a, x, lo=0, hi=None) # 返回要在列表a中插入项目x的索引,假设a已排序。 - 返回小于并最接近x的索引 # 返回值i是这样的:a[:i]中的所有e都有e <= x, a[i:]中的所有e都有e > x。 # 可选的args lo(默认0)和hi(默认len(a))绑定要搜索的a片。 li = [1, 2, 3, 4, 5] a = bisect.bisect(li, 3.6) print(a) # 3