代码之家  ›  专栏  ›  技术社区  ›  Fnord

大列表生成优化

  •  0
  • Fnord  · 技术社区  · 11 年前

    我需要一个python函数,它将以以下形式获取字符串列表:

    seq = ['A[0]','B[2:5]','A[4]']
    

    并返回一个具有保留顺序的“扩展”元素的新列表,如下所示:

    expanded = ['A[0]', 'B[2]', 'B[3]', 'B[4]', 'B[5]', 'A[4]']
    

    为了实现我的目标,我编写了一个简单的函数:

    def expand_seq(seq):
        #['item[i]' for item in seq for xrange in item]
        return ['%s[%s]'%(item.split('[')[0],i) for item in seq for i in xrange(int(item.split('[')[-1][:-1].split(':')[0]),int(item.split('[')[-1][:-1].split(':')[-1])+1)]
    

    当处理一个生成少于50万个项目的序列时,它工作得很好,但当生成非常大的列表(超过100万个)时,它会慢很多。例如:

    # let's generate 10 million items!
    seq = ['A[1:5000000]','B[1:5000000]']
    t1 = time.clock()
    seq = expand_seq(seq)
    t2 = time.clock()
    print round(t2-t1, 3)
    # RESULT: 9.541 seconds
    

    我正在寻找改进此功能的方法,并希望在处理大型列表时加快其速度。如果有人提出建议,我很乐意听到!

    2 回复  |  直到 11 年前
        1
  •  2
  •   JuniorCompressor    11 年前

    以下内容似乎可以提高35%的速度:

    import re
    
    r = re.compile(r"(\w+)\[(\d+)(?::(\d+))?\]")
    
    def expand_seq(seq):
        result = []
        for item in seq:
            m = r.match(item)
            name, start, end = m.group(1), int(m.group(2)), m.group(3)
            rng = xrange(start, int(end)) if end else (start,)
            t = name + "["
            result.extend(t + str(i) + "]" for i in rng)
        return result
    

    使用此代码:

    • 我们编译一个正则表达式用于函数。
    • 我们直接连接字符串。
        2
  •  0
  •   Andrew Magee    11 年前

    我不确定你会得到 戏剧性的 加速,因为无法从根本上改进算法。通过这样做,我确实比你的速度提高了20%:

    def expand_seq(seq):
        expanded = []
        for s in seq:
            name, indices = s[0:-1].split("[")
            if ":" in indices:
                index1, index2 = [int(i) for i in indices.split(":")]
            else:
                index1 = int(indices)
                index2 = index1
            for n in range(index1, index2 + 1):
                expanded.append("{}[{}]".format(name, n))
        return expanded
    

    我认为提速主要是因为没有重复一些操作(比如 int split )你必须这样做才能将你的解决方案保持在一行。

    正如已经建议的那样,如果使用生成器,您可以立即开始使用结果。这样地:

    def expand_seq(seq):
        for s in seq:
            name, indices = s[0:-1].split("[")
            if ":" in indices:
                index1, index2 = [int(i) for i in indices.split(":")]
            else:
                index1 = int(indices)
                index2 = index1
            for n in range(index1, index2 + 1):
                yield "{}[{}]".format(name, n)