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