前缀为短python列表的惯用语法是什么?

hur*_*lad 479 python list prepend

list.append()是添加到列表末尾的明显选择.这是对失踪人员的合理解释list.prepend().假设我的列表很短并且性能问题可以忽略不计,那就是

list.insert(0, x)
Run Code Online (Sandbox Code Playgroud)

要么

list[0:0] = [x]
Run Code Online (Sandbox Code Playgroud)

地道?

Ray*_*ger 696

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

无论何时看到它,都可能是时候考虑使用collections.deque而不是列表了.

  • @马特M。如果你在列表的前面插入,Python必须将所有其他项目向前移动一个空格,列表不能“在前面腾出空间”。collections.deque(双端队列)支持“在前面腾出空间”,并且在这种情况下速度要快得多。 (26认同)
  • “无论何时你看到它,可能是时候考虑使用 collections.deque 而不是列表了。” 为什么是这样? (15认同)
  • @fejfo,我认为评论应该是答案的一部分。 (11认同)
  • 就计算时间而言,“new_list = [x] + your_list”是否比“your_list.insert(x)”效率低? (2认同)

Nil*_*ler 230

如果你可以采用功能方式,以下内容非常清楚

new_list = [x] + your_list
Run Code Online (Sandbox Code Playgroud)

当然,你还没有插入xyour_list,而你已经创建了一个新的列表xpreprended它.

  • 虽然它不能满足这个问题,但它完善了,这就是本网站的目的.欣赏评论,你是对的,但是当人们搜索这个时,看到这个是有帮助的. (76认同)
  • 如您所见,这不是在列表前面.它正在创建一个新列表.因此它根本不满足这个问题. (41认同)
  • 此外,如果您想将列表添加到列表中,那么使用 insert 将无法按预期工作。但是这个方法可以! (4认同)
  • your_list = [x] + your_list 有什么问题吗?那不会创建一个新列表,不是吗? (2认同)
  • @lightbox142 它将创建一个新列表并将其分配给“your_list”。 (2认同)
  • 就计算时间而言,“new_list = [x] + your_list”是否比“your_list.insert(x)”效率低? (2认同)

Aar*_*all 74

前缀为短python列表的惯用语法是什么?

您通常不希望重复地在Python中添加前导列表.

如果它很,而你没有做很多......那么好吧.

list.insert

list.insert可以采用这种方式.

list.insert(0, x)
Run Code Online (Sandbox Code Playgroud)

但这是低效的,因为在Python中,a list是一个指针数组,Python现在必须将列表中的每个指针都向下移动一个,以便在第一个插槽中插入指向对象的指针,所以这实际上只是有效的对于相当短的清单,正如你所问.

如果你想要一个能够在前置元素上有效的容器,你需要一个双向链表.Python有一个 - 它叫做a deque.

deque.appendleft

A collections.deque有许多列表方法.list.sort是一个例外,deque最终不完全Liskov可替代list.

for (i = n; --i >= where; )
    items[i+1] = items[i];
Run Code Online (Sandbox Code Playgroud)

deque还有一个appendleft方法(以及popleft).这deque是一个双端队列和一个双向链表 - 无论长度如何,它总是花费相同的时间来预先制作一些东西.在大O表示法中,O(1)与列表的O(n)时间相比.这是用法:

>>> set(dir(list)) - set(dir(deque))
{'sort'}
Run Code Online (Sandbox Code Playgroud)

deque.extendleft

deque的extendleft方法也是相关的,迭代地预先设定:

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

请注意,每个元素将一次添加一个元素,从而有效地颠倒它们的顺序.

表现与list对比deque

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

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

和表现:

import timeit
from collections import deque

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

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

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

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

def deque_extendleft():
    d = deque()
    d.extendleft(range(20)) # semantically same as deque_appendleft above
Run Code Online (Sandbox Code Playgroud)

双端队列要快得多.随着列表越来越长,我希望deque能够表现得更好.如果你可以使用deque,extendleft那么你可能会获得最好的表现.


Ale*_*dov 56

如果有人发现像我这样的问题,这里是我提出的方法的性能测试:

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
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,insert切片分配几乎是显式添加的两倍,并且结果非常接近.正如Raymond Hettinger所指出insert的更常见的选择和我,个人更喜欢这种方式来预先列出.

  • 该测试缺少的一件事是复杂性.虽然前两个选项具有恒定的复杂性(当列表中有更多元素时它不会变慢),但第三个选项具有线性复杂性(它确实变慢,取决于列表中元素的数量),因为它总是必须复制整个列表.如果列表中包含更多元素,结果可能会变得更糟. (11认同)
  • @Dakkaron我觉得你错了.相当多的消息来源引用了list.insert的线性复杂性,例如[这个漂亮的表格](https://wiki.python.org/moin/TimeComplexity),并且提问者链接的合理解释暗示了这一点.我怀疑CPython在前两种情况下重新分配列表中内存中的每个元素,因此所有这三个元素都可能具有线性复杂性.我实际上没有看过代码或自己测试过,如果这些来源是错误的,那就很抱歉.Collections.deque.appendleft确实具有您所谈论的线性复杂性. (6认同)
  • 这些基准很糟糕。初始列表应在单独的设置步骤中创建,而不是计时本身的一部分。最后一个创建了一个长度为 1000001 的新列表,因此与其他两个就地变异版本相比的是苹果和橙子。 (3认同)

jos*_*nez 6

在我看来,在 Python 中将元素或列表添加到另一个列表的最优雅和惯用的方法是使用扩展运算符 *(也称为解包运算符),

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

# Modification
l = [1, 2, 3, *l]
Run Code Online (Sandbox Code Playgroud)

修改后的结果列表在哪里 [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
Run Code Online (Sandbox Code Playgroud)

我不喜欢其他常用方法l.insert(0, value),因为它需要一个幻数。此外,insert()只允许添加单个元素,但是上面的方法具有相同的语法来添加单个元素或多个元素。

  • 为何如此?:Smile: 在没有辩护律师在场的情况下我唯一想说的是“过早的优化是万恶之源”。正如我的答案的第一段所述,我指的是连接两个列表的惯用方式。 (2认同)