gitblog icon indicating copy to clipboard operation
gitblog copied to clipboard

从 Rich 作者的一个问题说起

Open yihong0618 opened this issue 3 years ago • 4 comments

前几天刷推,看到 Rich 的作者发了一条推特,这个问题相当的有趣,前几天在做 iBeats 的时候也遇到过类似的需求,手痒试试有没有好的解法。 image

我的解法还不错,还得到了作者的称赞,开心了一下~

推特的好处是能遇到各种有趣的解法和讨论,下面对每个解法的解释,其中不少 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 非常强大的切片了。 image image

有用同样方式的推友贴出了效率的比较 image

可以看出,如果不去 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, 就相当于不断的迭代,求和。 image 但因为也是循环迭代,当大列表时候存在效率问题

总结

从这一个问题动手研究,看大家的思路,还是非常有趣的,有些解法很巧妙也很好玩,参与其中也体会到了写 Python 的乐趣,虽然这些大部分的方式不会让我们在工作中写出更好的代码。但,研究这些方式背后的知识非常有趣,至少让我快乐了一个小时,把我的快乐分享给大家哈哈。

yihong0618 avatar Jun 21 '21 01:06 yihong0618

def merge_gen(a, b):
    for thing1, thing2 in zip(a, b):
          yield a
          yield b

是不是 yield thing1/2?

zjiekai avatar Jun 21 '21 01:06 zjiekai

def merge_gen(a, b):
    for thing1, thing2 in zip(a, b):
          yield a
          yield b

是不是 yield thing1/2?

我改下

yihong0618 avatar Jun 21 '21 01:06 yihong0618

分析得挺好的,简单来说就是python的list 不支持带capacity的init,这样就出现了在塞数据的时候需要做resize 然后copy 的情况。 本质上就是在避免 resize 这么一件事情。

Kilerd avatar Jul 01 '21 03:07 Kilerd

一看到长列表就想到了yield~ 只看了前半部分我还纳闷怎么没人用~ 呵呵

longqzh avatar Jul 18 '22 12:07 longqzh