用pop()列出Python中的操作

rec*_*gle 11 python list

简而言之,我需要根据索引从列表中删除多个项目.但是,我不能使用pop,因为它会改变索引(没有一些笨拙的补偿系统).有没有办法同时删除多个项目?

我有一个遍历列表的算法,如果条件正确,则通过pop方法删除该项.出现问题,因为这都是在循环中完成的.弹出完成后,列表将缩短一,将所有值替换为1.所以循环将超出范围.是否可以同时删除多个项目或其他解决方案?

我的问题的一个例子:

L = ['a', 'b', 'c', 'd']

for i in range(len(L)):
    print L
    if L[i] == 'a' or L[i] == 'c':
        L.pop(i)
Run Code Online (Sandbox Code Playgroud)

pho*_*oji 17

你的名单很大吗?如果是这样,请使用ifilterfrom itertools来过滤掉你不想懒惰的元素(没有前期费用).

列表不是那么大?只需使用列表理解:

 newlist = [x for x in oldlist if x not in ['a', 'c'] ]
Run Code Online (Sandbox Code Playgroud)

这将创建列表的新副本.除非您真的关心内存消耗,否则这通常不是效率问题.

作为语法方便和懒惰(=大型列表的效率)的快乐媒介,您可以使用( )而不是[ ]:来构造生成器而不是列表:

interestingelts = (x for x in oldlist if x not in ['a', 'c'])
Run Code Online (Sandbox Code Playgroud)

在此之后,您可以迭代interestingelts,但您无法索引它:

 for y in interestingelts:    # ok
    print y

 print interestingelts[0]     # not ok: generator allows sequential access only
Run Code Online (Sandbox Code Playgroud)


sen*_*rle 15

你想要一个列表理解:

L = [c for c in L if c not in ['a', 'c']]
Run Code Online (Sandbox Code Playgroud)

或者,如果您真的不想创建副本,请倒退:

for i in reversed(range(len(L))):
    if L[i] in ['a', 'c']:
        L.pop(i)    # del L[i] is more efficient
Run Code Online (Sandbox Code Playgroud)

感谢ncoghlan for reversed()&phooji的del L[i]建议.(我决定把它留下来L.pop(i),因为问题最初是如何形成的.)

此外,正如JS塞巴斯蒂安正确指出的那样,倒退是节省空间但时间效率低下的; 大多数情况下,列表理解或生成器(L = (...)而不是L = [...])是最好的.

编辑:

好的,所以因为人们似乎想要比上面的反转方法更慢得可笑(我无法想象为什么...... :)这里是一个保留顺序的就地过滤器,其速度应该与列表理解不同一个常数.(这类似于我想要在c中过滤字符串时我要做的事情.)

write_i = 0
for read_i in range(len(L)):
    L[write_i] = L[read_i]
    if L[read_i] not in ['a', 'c']:
         write_i += 1

del L[write_i:]
print L
# output: ['b', 'd']
Run Code Online (Sandbox Code Playgroud)

  • 既然你没有使用`L.pop(i)`的返回值,我们可以通过`del L [i]`来实现这个更高效的... (2认同)
  • @phooji:有时你需要进行修改,在这种情况下反向迭代索引绝对是正确的方法 (2认同)
  • @senderle:`reverse(range(len(L)))`比直接创建反转范围所需的倍数`-1'更容易阅读. (2认同)

jfs*_*jfs 7

摘要

  • 使用list comprehension(或genexpr)从列表中删除多个项目
  • 如果您的输入是一个大字节字符串,则用于str.translate()删除字符
  • del L[i]对于大型列表,一次删除一个项目的速度很慢

如果项目是您的示例中的字节,您可以使用str.translate():

def remove_bytes(bytestr, delbytes):
    """
    >>> remove_bytes(b'abcd', b'ac') == b'bd'
    True
    """
    return bytestr.translate(None, delbytes)
Run Code Online (Sandbox Code Playgroud)

通常,可以使用切片删除多个项目:

def remove_inplace_without_order(L, delitems):
    """Remove all items from `L` that are in `delitems` (not preserving order).

    >>> L = list(range(4)); remove_inplace_without_order(L, [0,2]); L
    [3, 1]
    """
    idel = len(L) # items idel.. to be removed
    for i in reversed(range(len(L))):
        if L[i] in delitems:
            idel -= 1
            L[i] = L[idel] # save `idel`-th item
    del L[idel:] # remove items all at once
    #NOTE: the function returns `None` (it means it modifies `L` inplace)
Run Code Online (Sandbox Code Playgroud)

正如@phooji@senderle已经提到过列表理解(或生成器表达式)在你的情况下更可取:

def remove_listcomp(L, delitems):
    return [x for x in L if x not in delitems]
Run Code Online (Sandbox Code Playgroud)

这是一个性能比较L=list("abcd"*10**5); delitems="ac":

| function                     | time, msec |  ratio |
|------------------------------+------------+--------|
| list                         |       4.42 |    0.9 |
| remove_bytes                 |       4.88 |    1.0 |
| remove                       |       27.3 |    5.6 |
| remove_listcomp              |       36.8 |    7.5 |
| remove_inplace_without_order |       71.2 |   14.6 |
| remove_inplace_senderle2     |       83.8 |   17.2 |
| remove_inplace_senderle      |      15000 | 3073.8 |
#+TBLFM: $3=$2/@3$2;%.1f
Run Code Online (Sandbox Code Playgroud)

哪里

try:
    from itertools import ifilterfalse as filterfalse
except ImportError:
    from itertools import filterfalse # py3k

def remove(L, delitems):
    return filterfalse(delitems.__contains__, L)

def remove_inplace_senderle(L, delitems):
    for i in reversed(range(len(L))):
        if L[i] in delitems:
            del L[i]

def remove_inplace_senderle2(L, delitems):
    write_i = 0
    for read_i in range(len(L)):
        L[write_i] = L[read_i]
        if L[read_i] not in delitems:
             write_i += 1
    del L[write_i:]
Run Code Online (Sandbox Code Playgroud)

remove_inplace_senderle()由于它使用O(N**2)算法很慢.每个都del L[i]可能导致向右移动所有项目以缩小差距.

由于某些算法修改了输入,因此上表中的时间列包括创建新输入列表(第一行)所需的时间.

这是相同输入的时间,但没有在每次迭代中创建新列表:

 | function        | time, msec | ratio |
 |-----------------+------------+-------|
 | remove_bytes    |      0.391 |     1 |
 | remove          |       24.3 |    62 |
 | remove_listcomp |       33.4 |    85 |
 #+TBLFM: $3=$2/@2$2;%d
Run Code Online (Sandbox Code Playgroud)

该表显示,itertools.ifilterfalse()与listcomp 相比,它没有提供显着的改进.

一般来说,考虑这些任务的性能是不值得的,甚至是有害的,除非探查器证明这个代码是瓶颈并且对你的程序很重要.但是,了解可以提供超过一个数量级的速度改进的替代方法可能是有用的.