2010-04-13 40 views
9

我的問題很簡單:我有一長串元素,我想要遍歷並檢查每個元素是否符合條件。根據條件的結果,我想刪除列表中的當前元素,並像往常一樣繼續迭代它。在迭代時從列表中刪除項目,而無需在Python中使用額外的內存

我已閱讀了有關此問題的其他一些線索。提出兩種解決方案。要麼從列表中創建一個字典(這意味着製作所有數據的副本,這些數據已經填滿了我的案例中的所有RAM)。要麼反過來列表(這打破了我想實現的算法的概念)。

有沒有比這更好或更優雅的方式來做到這一點?

def walk_list(list_of_g): 
    g_index = 0 
    while g_index < len(list_of_g): 
     g_current = list_of_g[g_index] 
     if subtle_condition(g_current): 
      list_of_g.pop(g_index) 
     else: 
      g_index = g_index + 1 
+2

所有這些的副本:http://stackoverflow.com/search?q=%5Bpython%5D+list+remove。具體http://stackoverflow.com/questions/1207406/remove-items-from-a-list-while-iterating-in-python – 2010-04-13 11:48:38

+2

是的我在發佈我的問題之前仔細閱讀你鏈接到的其他線程。在另一個線索中提出的答案沒有一個解決我關心的問題。這是因爲我的問題有所不同,即複製列表被排除在外並且向後遍歷。 – xApple 2010-04-13 13:41:09

回答

6

這裏是如果你非得從原來的列表中刪除項目的備選答案,你沒有足夠的內存來進行復印 - 移動該項目在列表中向下自己:

def walk_list(list_of_g): 
    to_idx = 0 
    for g_current in list_of_g: 
     if not subtle_condition(g_current): 
      list_of_g[to_idx] = g_current 
      to_idx += 1 
    del list_of_g[to_idx:] 

這將移動每一個項目(實際上是一個指針,以每個項目)正好一次,所以會爲O(N)。函數結尾處的del語句將刪除列表末尾的所有不需要的項目,我認爲Python足夠智能,可以調整列表的大小,而無需爲列表的新副本分配內存。

+0

我喜歡它。即使它不是「Pythonic」,它回答了這個問題,它仍然非常漂亮。 – zildjohn01 2011-07-12 00:36:16

1

這個怎麼樣?

[x for x in list_of_g if not subtle_condition(x)] 

其回報與subtle_condition

1

例外,爲了簡單起見新的列表,使用列表理解:

def walk_list(list_of_g): 
    return [g for g in list_of_g if not subtle_condition(g)] 

當然,這不會改變原來的列表,因此調用代碼將不得不不同。

如果你真的想變異列表(很少的最佳選擇),倒着走路更簡單:

def walk_list(list_of_g): 
    for i in xrange(len(list_of_g), -1, -1): 
     if subtle_condition(list_of_g[i]): 
      del list_of_g[i] 
13
li = [ x for x in li if condition(x)] 

li = filter(condition,li) 

Thanks to Dave Kirby

+2

正如Alex Martelli在http://stackoverflow.com/a/1208792/914874中建議的那樣:li [:] = [x for li如果條件(x)]是更好的方法。 – Eduardo 2013-07-05 11:56:57

+0

@Eduardo有趣!非常感謝! – 2013-07-05 19:45:52

4

內置-in過濾功能只是爲了做到這一點:

list_of_g = filter(lambda x: not subtle_condition(x), list_of_g) 
6

從列表中刪除項目很昂貴,因爲python必須將g_index上面的所有項目複製到一個地方。如果要刪除的項目數量與列表N的長度成比例,則算法將爲O(N ** 2)。如果列表足夠長以填滿RAM,那麼您將等待很長時間才能完成。

更有效的創建列表的篩選副本,或者使用列表理解爲馬塞洛表現,或者使用過濾器或itertools.ifilter功能:

g_list = filter(not_subtle_condition, g_list) 

如果您不需要使用新的列表,並只希望一次迭代它,那麼它最好使用IFilter的,因爲這將不會創建第二個列表:

for g_current in itertools.ifilter(not_subtle_condtion, g_list): 
    # do stuff with g_current 
1

聽起來像一個很好的用例的過濾功能。

def should_be_removed(element): 
    return element > 5 

a = range(10) 
a = filter(should_be_removed, a) 

但是,這將不會在迭代時刪除列表(我也不推薦它)。如果內存空間(或其他性能方面的原因),你真的需要它,你可以做到以下幾點:

i = 0 
while i < len(a): 
    if should_be_removed(a[i]): 
     a.remove(a[i]) 
    else: 
     i+=1 
    print a 
+0

按元素刪除可能會更慢或更快,具體取決於列表的內容。 – Alcides 2010-04-13 11:56:13

0

如果執行反向迭代,你可以刪除的飛行元素,而不會影響你下次訪問指數:

numbers = range(20) 

# remove all numbers that are multiples of 3 
l = len(numbers) 
for i, n in enumerate(reversed(numbers)): 
    if n % 3 == 0: 
     del numbers[l - i - 1] 

print numbers 

enumerate(reversed(numbers))只是風格上的選擇。如果你需要旅行,以便列表

l = len(numbers) 
for i in range(l-1, -1, -1): 
    n = numbers[i] 
    if n % 3 == 0: 
     del numbers[i] 

,可以前後顛倒迭代後.reverse()扭轉它在的地方:你可以使用一個範圍,如果這是更清晰給你。這不會重複您的列表。

相關問題