[英]How to remove the last occurrence of an item from a list?
list.remove() 函数用于删除第一次出现在列表中的项目。 有没有内置函数可以删除最后一次? 例如,如果我有一个列表,请说:
X = ['sf', 'cc', 'ch', 'sc', 'sh', 'ch']
并且我想从列表中删除最后一个“ch”,是否有比我目前正在做的更好的方法,即:
X.reverse()
X.remove('ch')
X.reverse()
我很快也将不得不担心被删除的项目可能不在列表中的情况。 因此,在这种情况下不会抛出错误的方法将是首选。
if 'ch' in X:
X.reverse()
X.remove('ch')
X.reverse()
最pythonic的方法是try: except
remove:
X.reverse()
try:
X.remove('ch')
except:
pass
X.reverse()
根据您对速度的评论,这两种方法都是 O(N),因为x in list
和list.reverse()
都是 O(N),因此它们之间没有太多关系。 如果您希望元素通常在那里,您可以使用 try: catch 将x in list
保存x in list
检查中,但是如果您希望它通常不在那里,您可以通过首先检查成员资格来保存 2 个reverse()
s。
你的代码真的没有任何问题。 它有效,很清楚它为什么有效,很难出错或误解。
是的,你可以让它更快,但只能通过一个常数因子。 (你的算法做了两个reverse
s,每个N
步,一个remove
,这是N-1
步,所以O(N)
。由于你的数据没有排序或任何可以帮助我们更快地找到值的东西,很明显,理想的算法也是O(N)
。)并且以使其更复杂为代价。
显然可能更快的方法是从末尾手动迭代,直到找到一个值,然后删除该值。 这也避免了必须处理ValueError
。 使用enumerate
可能会有所帮助……但正确使用(不复制整个内容)可能会很棘手。
因此,让我们将这些与您现有的代码进行比较,都将其包装在try
/ except
和if
:
def f_rev_ex(xs, s):
xs.reverse()
try:
xs.remove(s)
except ValueError:
pass
xs.reverse()
def f_rev_if(xs, s):
if s in xs:
xs.reverse()
xs.remove(s)
xs.reverse()
def f_for(xs, s):
for i in range(len(xs)-1, -1, -1):
if s == xs[i]:
del xs[i]
break
def f_enum(xs, s):
for i, x in reversed(list(enumerate(xs))):
if x == s:
del xs[i]
break
对于像你这样小的列表,测试甚至不值得运行,所以我发明了自己的随机数据(当然,在现实生活中你必须知道你的数据):
In [58]: xs = [random.choice(string.ascii_lowercase) for _ in range(10000)]
In [59]: %timeit y = x[:]; f_rev_ex(y, 'a')
10000 loops, best of 3: 34.7 µs per loop
In [60]: %timeit y = x[:]; f_rev_if(y, 'a')
10000 loops, best of 3: 35.1 µs per loop
In [61]: %timeit y = x[:]; f_for(y, 'a')
10000 loops, best of 3: 26.6 µs per loop
In [62]: %timeit y = x[:]; f_enum(y, 'a')
1000 loops, best of 3: 604 µs per loop
嗯,最后一个不是一个很好的主意……但另一个比我们开始的速度快 25%。 因此,我们在比实际数据大 4 个数量级的数据上节省了整整 9 微秒。 这取决于你是否值得使用可读性较差、更容易搞砸的代码。 (而且我不会在不复制的情况下向您展示我的基于enumerate
的实现,因为我弄错了。:P)
生成一个反向列表,保留原始索引并删除您找到的第一个实例。
X = ['sf', 'cc', 'ch', 'sc', 'sh', 'ch']
print X
for i, e in reversed(list(enumerate(X))):
if e == 'ch':
del X[i]
break
print X
如果它没有找到字符串,它就不会改变列表。
首先,您可以使用if in
语句检查该项目是否在列表if in
。 然后您可以反转列表并删除元素。
if "ch" in X:
X.reverse()
X.remove("ch")
X.reverse()
没有 reverse() 并且类似于上面的一个答案:
def RightRemove(alist, x):
for i in range(len(alist), 0, -1): # from end to begin
if alist[i-1] == x: # element x exists
alist.pop(i-1) # remove it
break # return
还有一个答案...
def remove_last_occurrence(lst, element):
'''
Removes the last occurrence of a given element in a list (modifies list in-place).
:return bool:
True if the element was found and False otherwise.
'''
for i, s in enumerate(reversed(lst)):
if s == element:
del lst[len(lst) - 1 - i]
return True
return False
还有一个..
def remove_last_occurrence_one_liner(lst, element):
"""
Removes the last occurrence of a given element in a list (modifies list in-place).
Raises same exception than lst.index(element) if element can not be found.
"""
del lst[len(lst) - lst[::-1].index(element) - 1]
但它并没有击败abarnert的 for 循环
x = [random.choice(string.ascii_lowercase) for _ in range(10000)]
%timeit y = x[:]; f_rev_ex(y, 'a')
34.3 µs ± 219 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
%timeit y = x[:]; f_rev_if(y, 'a')
34.9 µs ± 195 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
%timeit y = x[:]; f_for(y, 'a')
26.9 µs ± 109 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
%timeit y = x[:]; f_enum(y, 'a')
699 µs ± 4.86 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit y = x[:]; remove_last_occurrence_one_liner(y, 'a')
49 µs ± 375 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
这是一个更简单的函数,用于删除元素在列表中的最后一次出现(如果找到):
def right_remove(l: list, element: Any) -> None:
try:
index = l[::-1].index(element)
except ValueError:
return
l.pop(len(l) - index - 1)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.