list.append()附加到列表的末尾。这解释了list.prepend()不存在的原因,因为考虑到大型列表的性能。对于一个短列表,我如何预先一个值?


s.insert(0, x)形式是最常见的。

无论何时看到它,都应该考虑使用collections.deque而不是列表。deque的前置操作在常数时间内运行。列表的前置操作以线性时间运行。


这将创建一个带x的新列表,而不是修改现有的列表:

new_list = [x] + old_list

如果有人像我一样发现了这个问题,下面是我对所提方法的性能测试:

Python 2.7.8

In [1]: %timeit ([1]*1000000).insert(0, 0)
100 loops, best of 3: 4.62 ms per loop

In [2]: %timeit ([1]*1000000)[0:0] = [0]
100 loops, best of 3: 4.55 ms per loop

In [3]: %timeit [0] + [1]*1000000
100 loops, best of 3: 8.04 ms per loop

正如你所看到的,插入和切片赋值的速度几乎是显式添加的两倍,并且结果非常接近。正如Raymond Hettinger所指出的那样,插入是更常见的选项,我个人更喜欢这种方式来添加列表。


一个简短的python列表的前缀的惯用语法是什么?

在Python中,通常不希望重复地在列表前加上前缀。

如果清单很短,而且你做的不多……然后点击ok。

list.insert

列表中。Insert可以这样使用。

list.insert(0, x)

但这是低效的,因为在Python中,列表是一个指针数组,Python现在必须取列表中的每个指针并将其向下移动1,以将指针插入到第一个槽中的对象,所以这实际上只对相当短的列表有效。

这是CPython源代码中的一个代码片段,在这里实现了这个功能——正如你所看到的,我们从数组的末尾开始,每次插入都将所有内容下移1:

for (i = n; --i >= where; )
    items[i+1] = items[i];

如果你想要一个容器/列表,它可以有效地预挂元素,你需要一个链表。Python有一个双链表,它可以快速插入开头和结尾——它被称为deque。

deque.appendleft

collections.deque具有列表的许多方法。列表。sort是个例外,这使得deque绝对不能完全用Liskov方法代替list。

>>> set(dir(list)) - set(dir(deque))
{'sort'}

deque还有一个appendleft方法(以及popleft方法)。deque是一个双端队列和一个双链表——无论长度如何,它总是花费相同的时间来预前缀一些东西。在大O符号中,O(1) vs O(n)时间用于列表。用法如下:

>>> import collections
>>> d = collections.deque('1234')
>>> d
deque(['1', '2', '3', '4'])
>>> d.appendleft('0')
>>> d
deque(['0', '1', '2', '3', '4'])

deque.extendleft

同样相关的还有deque的extendleft方法,它迭代地前置:

>>> from collections import deque
>>> d2 = deque('def')
>>> d2.extendleft('cba')
>>> d2
deque(['a', 'b', 'c', 'd', 'e', 'f'])

注意,每个元素将一次前置一个元素,从而有效地颠倒它们的顺序。

list相对于deque的性能

首先,我们设置了一些迭代前置:

import timeit
from collections import deque


def list_insert_0(prepends: int):
    l = []
    for i in range(prepends):
        l.insert(0, i)

def list_slice_insert(prepends):
    l = []
    for i in range(prepends):
        l[:0] = [i]      # semantically same as list.insert(0, i)

def list_add(prepends):
    l = []
    for i in range(prepends):
        l = [i] + l      # caveat: new list each time

def deque_appendleft(prepends):
    d = deque()
    for i in range(prepends):
        d.appendleft(i)  # semantically same as list.insert(0, i)

def deque_extendleft(prepends):
    d = deque()
    d.extendleft(range(prepends)) # semantically same as deque_appendleft above

还有一个用于分析的函数,这样我们就可以公平地比较一系列用法中的所有操作:

def compare_prepends(n, runs_per_trial):
    results = {}
    for function in (
        list_insert_0, list_slice_insert,
        list_add, deque_appendleft, deque_extendleft,
        ):
        shortest_time = min(timeit.repeat(
            lambda: function(n), number=runs_per_trial))
        results[function.__name__] = shortest_time
    ranked_methods = sorted(results.items(), key=lambda kv: kv[1])
    for name, duration in ranked_methods:
        print(f'{name} took {duration} seconds')

性能(调整每次试验的运行次数,以弥补更多prepends的较长运行时间-默认情况下重复做三次试验):

compare_prepends(20, 1_000_000)
compare_prepends(100, 100_000)
compare_prepends(500, 100_000)
compare_prepends(2500, 10_000)
>>> compare_prepends(20, 1_000_000)
deque_extendleft took 0.6490256823599339 seconds
deque_appendleft took 1.4702797569334507 seconds
list_insert_0 took 1.9417422469705343 seconds
list_add took 2.7092894352972507 seconds
list_slice_insert took 3.1809083241969347 seconds
>>> compare_prepends(100, 100_000)
deque_extendleft took 0.1177942156791687 seconds
deque_appendleft took 0.5385235995054245 seconds
list_insert_0 took 0.9471780974417925 seconds
list_slice_insert took 1.4850486349314451 seconds
list_add took 2.1660344172269106 seconds
>>> compare_prepends(500, 100_000)
deque_extendleft took 0.7309095915406942 seconds
deque_appendleft took 2.895373275503516 seconds
list_slice_insert took 8.782583676278591 seconds
list_insert_0 took 8.931685039773583 seconds
list_add took 30.113558700308204 seconds
>>> compare_prepends(2500, 10_000)
deque_extendleft took 0.4839253816753626 seconds
deque_appendleft took 1.5615574326366186 seconds
list_slice_insert took 6.712615916505456 seconds
list_insert_0 took 13.894083382561803 seconds
list_add took 72.1727528590709 seconds

deque要快得多。当列表变长时,deque执行得更好。如果你可以使用deque的extendleft,你可能会得到最好的性能。

如果您必须使用列表,请记住,对于小列表,列表。插入工作更快,但对于较大的列表,使用切片符号进行插入会更快。

不要在列表前加前缀

清单是用来附加的,而不是前面的。如果你遇到这种情况,这种前置会损害你的代码的性能,要么切换到deque,或者,如果你可以逆转你的语义并达到同样的目标,逆转你的列表,而不是追加。

一般情况下,避免在内置的Python列表对象前加上前缀。


在我看来,在Python中,将一个元素或列表前置到另一个列表的最优雅和惯用的方法是使用展开操作符*(也称为解包操作符),

# Initial list
l = [4, 5, 6]

# Modification
l = [1, 2, 3, *l]

其中修改后的结果列表为[1,2,3,4,5,6]

我还喜欢用运算符+简单地将两个列表组合在一起,如下所示,

# Prepends [1, 2, 3] to l
l = [1, 2, 3] + l

# Prepends element 42 to l
l = [42] + l

我不喜欢另一种常见的方法,l.insert(0, value),因为它需要一个神奇的数字。此外,insert()只允许在单个元素前加上前缀,但是上面的方法在单个元素或多个元素前加上前缀时具有相同的语法。


我们来看4种方法

使用insert ()

>>> 
>>> l = list(range(5))
>>> l
[0, 1, 2, 3, 4]
>>> l.insert(0, 5)
>>> l
[5, 0, 1, 2, 3, 4]
>>> 

使用[]和+

>>> 
>>> l = list(range(5))
>>> l
[0, 1, 2, 3, 4]
>>> l = [5] + l
>>> l
[5, 0, 1, 2, 3, 4]
>>> 

使用切片

>>> 
>>> l = list(range(5))
>>> l
[0, 1, 2, 3, 4]
>>> l[:0] = [5]
>>> l
[5, 0, 1, 2, 3, 4]
>>> 

使用collections.deque.appendleft ()

>>> 
>>> from collections import deque
>>> 
>>> l = list(range(5))
>>> l
[0, 1, 2, 3, 4]
>>> l = deque(l)
>>> l.appendleft(5)
>>> l = list(l)
>>> l
[5, 0, 1, 2, 3, 4]
>>> 

我会在python >= 3.0中做一些非常快的事情

list=[0,*list]

这可能不是最有效的方法,但在我看来这是最python化的方法。