【问题标题】:python list concatenation efficiencypython列表连接效率
【发布时间】:2012-08-18 18:14:22
【问题描述】:

在以下情况下连接两个列表list_alist_b 的最有效方法是什么:

  • list_b items 必须放在list_a items 之前
  • 结果必须放在list_a

我想到了 4 种可能性:

# 1
list_a = list_b + list_a

# 2
for item in list_b:
    list_a.insert(0, item)

# 3
for item in self.list_a:
    list_b.append(item)
list_a = list_b

# 4
list_a[0:0] = list_b

谢谢!

【问题讨论】:

  • 如果你想经常这样做或处理非常大的数据,有更好的数据结构(一个简单的:存储你所有的列表反转)。

标签: python list


【解决方案1】:

这是answer of BigYellowCactus 中使用的计时如何随着列表长度的增加而变化的图表。纵轴是初始化两个列表并在另一个之前插入一个列表所需的时间,以 usec 为单位。横轴是列表中的项目数。

t1

list_a = list_b + list_a

t2

for item in list_b:
    list_a.insert(0, item)

t3

for item in list_a:
    list_b.append(item)
list_a = list_b

t4

list_a[0:0] = list_b

【讨论】:

  • 我知道这个问题很久以前就被问过了,我想知道:如果我只是将 listb 添加到 lista....so a then b...这种情况仍然成立吗?还是有更有效的方法可以将 n 个列表添加到 1 个列表中?
  • 这张图表是错误的。它创建了list_a,它绝对支配了两种最快方法的运行时间,list_a = list_b + list_alist_a[0:0] = list_b
【解决方案2】:

鉴于

list_a = list_b + list_a

适用于您的目的,因此您实际上并不需要 list_a 对象本身来将所有数据存储在 list_a 中 - 您只需要它称为 list_a(即,您没有或不关心您浮动的任何其他变量,这些变量可能引用同一个列表)。

如果你也碰巧不关心它是否是一个列表,而只是关心它是可迭代的,那么你可以使用itertools.chain

list_a = itertools.chain(list_b, list_a)

如果您确实关心某些列表内容,您可以构建与chain 类似的事物类型,其行为类似于列表 - 类似于:

class ListChain(list):
    def __init__(self, *lists):
        self._lists = lists

    def __iter__(self):
        return itertools.chain.from_iterable(self._lists)

    def __len__(self):
        return sum(len(l) for l in self._lists)

    def append(self, item):
        self._lists[-1].append(item)

    def extend(self, iterable):
        self._lists.append(list(iterable))

    def __getitem__(self, item):
       for l in self._lists:
           if item < len(l):
              return l[item]
           item -= len(l)
       else:
          raise IndexError

等等。这需要付出很多努力(可能超过其价值)才能在所有情况下工作 - 例如,处理切片和负索引浮现在脑海中。但是对于非常简单的情况,这种方式可以避免大量复制列表内容。

【讨论】:

  • 有见地的回答!但在我的情况下没有用。 list_a 来自一个我不想搞砸的 3rdParty 模块。
【解决方案3】:

您可以将 list_b 分配给一个切片,该切片恰好为空,但位于 list_a 的开头:

list_a[0:0] = list_b

这是在任意位置将一个列表插入另一个列表的最快方法。

【讨论】:

  • 如何使用此方法追加到列表末尾
  • @PiyushDivyanakar:使用list.extend() 添加到列表的末尾。您也可以使用list_a[len(list_a):len(list_a)] 分配到末尾,但是编写、读取和执行要繁琐得多。
【解决方案4】:

试试这个:

list_a[0:0] = list_b

【讨论】:

    【解决方案5】:

    为什么不只是timeit

    import timeit
    
    create_data = """\
    list_a = range(10)
    list_b = range(10)
    """
    
    t1 = timeit.Timer(stmt=create_data + """\
    list_a = list_b + list_a
    """)
    
    t2 = timeit.Timer(create_data + """\
    for item in list_b:
        list_a.insert(0, item)
    """)
    
    t3 = timeit.Timer(create_data + """\
    for item in list_a:
        list_b.append(item)
    list_a = list_b
    """)
    
    t4 = timeit.Timer(create_data + """\
    list_a[0:0] = list_b
    """)
    
    for i, t in enumerate([t1,t2,t3,t4]):
        print i, "%.2f usec/pass" % (1000000 * t.timeit(number=100000)/100000)
    

    结果:

    0 0.73 微秒/通
    1 2.79 微秒/通
    2 1.66 微秒/通
    3 0.77 微秒/通过

    【讨论】:

    • 您现在也在为列表的创建计时。最好将其移至设置而不是定时部分。但是因为你必须每次都重新设置输入,所以这不会有太大的不同..
    【解决方案6】:

    itertools.chain 只是制作了一个生成器,因此,如果您可以不用使用生成器而不是列表,那么生成的时间是固定的,但是您在访问每个元素时会付出代价。否则 list_a[0:0] = list_blist_a = list_b + list_a 快大约 6 倍

    我认为list_a = list_b + list_a 是最易读的选择,而且已经相当快了。

    您提到的在for 循环中使用append() 的两种方法非常慢,因此我没有费心将它们包括在内。


    在具有 16 GB 2133 MHz LPDDR3 RAM 的 1.6 GHz 双核 Intel Core i5 上使用 Python 3.7.5 [Clang 11.0.0 (clang-1100.0.33.8)] on darwin 运行,使用以下代码:

    from timeit import timeit
    import random
    import matplotlib.pyplot as plt
    
    num_data_points = 1000
    step = 10
    methods = [
        # ordered from slowest to fastest to make the key easier to read
        # """for item in list_a: list_b.append(item); list_a = list_b""",
        # """for item in list_b: list_a.insert(0, item)""",
        # "list_a = list(itertools.chain(list_b, list_a))",
        "list_a = list_b + list_a",
        "list_a[0:0] = list_b",
        "list_a = itertools.chain(list_b, list_a)",
    ]
    
    x = list(range(0, num_data_points * step, step))
    y = [[] for _ in methods]
    for i in x:
        list_a = list(range(i))
        list_b = list(range(i))
        random.shuffle(list_a)
        random.shuffle(list_b)
        setup = f"list_a = {list_a}; list_b = {list_b}"
        for method_index, method in enumerate(methods):
            y[method_index].append(timeit(method, setup=setup, number=30))
        print(i, "out of", num_data_points * step)
    
    ax = plt.axes()
    for method_index, method in enumerate(methods):
        ax.plot(x, y[method_index], label=method)
    ax.set(xlabel="number of elements in both lists", ylabel="time (s) (lower is better)")
    ax.legend()
    plt.show()
    

    【讨论】:

    • 我发布了这个答案,因为接受的答案中的时间是错误的,列表的创建不应该是时间的一部分。
    猜你喜欢
    • 2012-07-09
    • 2013-08-28
    • 2011-08-29
    • 1970-01-01
    • 2012-07-09
    • 1970-01-01
    • 1970-01-01
    • 2010-11-20
    • 1970-01-01
    相关资源
    最近更新 更多