繁体   English   中英

如何从列表中删除最后一次出现的项目?

[英]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 listlist.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 / exceptif

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM