Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

从 Rich 作者的一个问题说起 #212

Open
yihong0618 opened this issue Jun 21, 2021 · 4 comments
Open

从 Rich 作者的一个问题说起 #212

yihong0618 opened this issue Jun 21, 2021 · 4 comments
Labels
技术文章 技术文章

Comments

@yihong0618
Copy link
Owner

yihong0618 commented Jun 21, 2021

前几天刷推,看到 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 yihong0618 added the 技术文章 技术文章 label Jun 21, 2021
@zjiekai
Copy link

zjiekai commented Jun 21, 2021

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

是不是 yield thing1/2?

@yihong0618
Copy link
Owner Author

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

是不是 yield thing1/2?

我改下

@Kilerd
Copy link

Kilerd commented Jul 1, 2021

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

@longqzh
Copy link

longqzh commented Jul 18, 2022

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

Repository owner deleted a comment from webpon Oct 14, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
技术文章 技术文章
4 participants