简而言之,我需要根据索引从列表中删除多个项目.但是,我不能使用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)
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 相比,它没有提供显着的改进.
一般来说,考虑这些任务的性能是不值得的,甚至是有害的,除非探查器证明这个代码是瓶颈并且对你的程序很重要.但是,了解可以提供超过一个数量级的速度改进的替代方法可能是有用的.
| 归档时间: |
|
| 查看次数: |
30409 次 |
| 最近记录: |