Python 容器使用的 5 个技巧和 2 个误区( 四 )


  • 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 。整个函数的控制流就能被大大简化:
     
    1. import bisect


    2.  
    3. # BREAKPOINTS 必须是已经排好序的,不然无法进行二分查找
    4. BREAKPOINTS = (1, 60, 3600, 3600 * 24)
    5. TMPLS = (
    6. # unit, template
    7. (1, "less than 1 second ago"),
    8. (1, "{units} seconds ago"),
    9. (60, "{units} minutes ago"),
    10. (3600, "{units} hours ago"),
    11. (3600 * 24, "{units} days ago"),
    12. )


    13.  
    14. def from_now(ts):
    15. """接收一个过去的时间戳,返回距离当前时间的相对时间文字描述
    16. """
    17. seconds_delta = int(time.time - ts)
    18. unit, tmpl = TMPLS[bisect.bisect(BREAKPOINTS, seconds_delta)]
    19. return tmpl.format(units=seconds_delta // unit)
    除了用元组可以优化过多的 if/else分支外,有些情况下字典也能被用来做同样的事情 。关键在于从现有代码找到重复的逻辑与规律,并多多尝试 。
     
    2. 在更多地方使用动态解包动态解包操作是指使用 ***运算符将可迭代对象“解开”的行为,在 Python 2 时代,这个操作只能被用在函数参数部分,并且对出现顺序和数量都有非常严格的要求,使用场景非常单一 。
     
    1. def calc(a, b, multiplier=1):
    2. return (a + b) * multiplier


    3.  
    4. # Python2 中只支持在函数参数部分进行动态解包
    5. print calc(*[1, 2], **{"multiplier": 10})
    6. # OUTPUT: 30
    不过,Python 3 尤其是 3.5 版本后, ***的使用场景被大大扩充了 。举个例子,在 Python 2 中,如果我们需要合并两个字典,需要这么做:
     
    1. def merge_dict(d1, d2):
    2. # 因为字典是可被修改的对象,为了避免修改原对象,此处需要复制一个 d1 的浅拷贝
    3. result = d1.copy
    4. result.update(d2)
    5. return result

    6. user = merge_dict({"name": "piglei"}, {"movies": ["Fight Club"]})
    但是在 Python 3.5 以后的版本,你可以直接用 **运算符来快速完成字典的合并操作:
     
    1. user = {**{"name": "piglei"}, **{"movies": ["Fight Club"]}}
    除此之外,你还可以在普通赋值语句中使用 *运算符来动态的解包可迭代对象 。如果你想详细了解相关内容,可以阅读下面推荐的 PEP 。
    Hint:推进动态解包场景扩充的两个 PEP: