从 Rich 作者的一个问题说起
前几天刷推,看到 Rich 的作者发了一条推特,这个问题相当的有趣,前几天在做 iBeats 的时候也遇到过类似的需求,手痒试试有没有好的解法。
我的解法还不错,还得到了作者的称赞,开心了一下~
推特的好处是能遇到各种有趣的解法和讨论,下面对每个解法的解释,其中不少 Python 的巧妙应用
基础方式
def merge(a, b):
res = []
for ab in zip(ab):
res.extend(ab)
return res
这种的解法的好处是清晰易懂,坏处是因为会不断的 resize list 当列表很大的时候会有效率问题
基础方式2
from itertools import chain
chain.from_iterable(zip(a,b))
这种是比较 Pythonic 的解法,用到了 Python 标准库中非常好用的 itertools, 且不用引入中间变量,没有特别的效率问题,清晰直观,也是工作中比较推荐的代码。
我的解法
以上的都有循环或需要不断 resize 的小问题,那么能不能不 resize 呢?如果列表的长度相同,是可以的,我们只需要建一个列表,然后把 a, b 的元素按规定放进去就行了,这就用到了 Python 非常强大的切片了。
有用同样方式的推友贴出了效率的比较
可以看出,如果不去 resize 的话会有将近 10 倍的效率
更好的解法
def merge_gen(a, b):
for thing1, thing2 in zip(a, b):
yield thing1
yield thing2
list(merge_gen(a, b))
用到了生成器,还不需要引入中间变量,效率同样很高
有趣的解法
sum(zip(a, b), ())
这个解法的有趣的地方在于,sum 是有第二个参数的,作为起始,默认是 0, 如果我们把第二个参数放一个空的 tuple, 就相当于不断的迭代,求和。 但因为也是循环迭代,当大列表时候存在效率问题
总结
从这一个问题动手研究,看大家的思路,还是非常有趣的,有些解法很巧妙也很好玩,参与其中也体会到了写 Python 的乐趣,虽然这些大部分的方式不会让我们在工作中写出更好的代码。但,研究这些方式背后的知识非常有趣,至少让我快乐了一个小时,把我的快乐分享给大家哈哈。