2016-02-29 97 views
0

我想從列表中刪除基於他們的HP的班級。我正在製作D & D廣告系列的大型戰鬥模擬器。它是一個簡單的程序,可以創建兩個類的列表並將它們彼此對齊。從列表中刪除班級

在解決死亡戰士問題時,我遇到了一個問題。如果一個戰鬥機在一輪中死亡,它會很好地工作,但是當多個死亡時,它會變得不穩定。

def check_human_deaths(): 
    for i in range(len(goodguys)): 
     if goodguys[i].hp <= 0: 
      print('{} has died...'.format(goodguys[i].name)) 
      goodguys.remove(goodguys[i]) 

卸下死戰鬥機改變列表的長度,拋指數錯誤:

IndexError: list index out of range 

我不知道如何從戰場去除死進行。任何提示都表示讚賞。讓我知道我是否以一種根本錯誤的方式解決這個問題。

+5

在遍歷它們時不要更改列表。 – Jasper

+7

http://stackoverflow.com/questions/1207406/remove-items-from-a-list-while-iterating-in-python –

回答

0

你會得到這個錯誤,因爲你正在迭代列表的長度,比如說l。每次您撥打list.remove()時,列表的長度會減少1,並且您的程序會提供索引錯誤,因爲list[l]不存在。相反,你可以使用:

def check_human_deaths(): 
    for goodguy in list(goodguys): 
     if goodguy.hp <= 0: 
      print('{} has died...'.format(goodguy.name)) 
      goodguys.remove(goodguy) 

注:當對象被從列表中刪除list(goodguys)將創建goodguys列表防止for循環的怪異行爲的副本:

>>> t= [1,2,3] 
>>> x= list(t) 
>>> x.remove(1) 
>>> x 
[2, 3] 
>>> t 
[1, 2, 3] 

即使從移除後的值x,這個值仍然會出現在t中,並且您的for循環將不會表現怪異

+0

你是對的。我沒有檢查過list()是否執行對象的深層副本,或者只是複製引用和'global'' goodguys'的可能問題,但是我的最後一點是不正確的。 –

+1

只是爲了糾正你,'list()'不執行'deep copy',而是執行'shallow copy'。列表中的類對象將保持不變。 'list()'等同於'copy.copy()',對於python中的深拷貝,我們有'copy.deepcopy()'(甚至會在列表中創建類對象的副本)。 –

+0

更少的更正和更多的澄清。謝謝:) 對整個問題的答案是肯定的。 –

1

問題是,當您從goodguys中刪除時,索引會減少一個。例如:

1,2,3,4 

刪除2

1,3,4 

三個指數已減一和規模已經減一。

3

兩個選擇:

修改名單的副本,並使用的,作爲新的列表結果:

>>> lst = [1,2,3,4,5] 
>>> for i in lst[:]: 
...  if i % 2: 
...   result.append(i) 
... 
>>> lst = result 
>>> lst 
[1, 3, 5] 

修改到位名單,但在相反的,以避免搞亂這樣做索引:

>>> lst = [1,2,3,4,5] 
>>> for i in lst[::-1]: 
...  if not i % 2: 
...   lst.remove(i) 
... 
>>> lst 
[1, 3, 5] 
+0

'lst [:] = [']'會是一個更好的選擇 –

+0

對不起,我現在正在密集 - 這是怎麼回事? – bgporter

+0

效率更高,儘管OP可能希望隨時打印,在這種情況下,「我反轉(lst)」將至少避免複製。 –

0

正如其他人所說,您不能在迭代它時更改列表,因爲奇怪的事情會發生。相反,你可以保存你要在循環後刪除,並刪除它們的元素:

def check_human_deaths(): 
    things_to_delete = [] 
    for i in range(len(goodguys)): 
     if goodguys[i].hp <= 0: 
      print('{} has died...'.format(goodguys[i].name)) 
      things_to_delete.append(goodguys[i]) 
    goodguys = [x for x in goodguys if x not in things_to_delete] 
1
goodguys = [ guy for guy in goodguys if guy.hp > 0 ] 

這將陣列中過濾掉任何死goodguys。

您可以在函數中使用這樣的:

def check_human_deaths(): 
    global goodguys 
    dedguys = [ guy for guy in goodguys if guy.hp <= 0 ] 
    for guy in dedguys: 
     print('{} has died...'.format(guy.name)) 
    goodguys = [ guy for guy in goodguys if guy.hp > 0 ] 
0

您可以使用發生器功能,讓您以更有效和正確的方式打印並更新列表做一個單一的做越過列表:

class Foo(): 
    def __init__(self, hp, name): 
     self.hp = hp 
     self.name = name 


def check_human_deaths(goodguys): 
    for guy in goodguys: 
     if guy.hp < 1: 
      print('{} has died...'.format(guy.name)) 
     else: 
      yield guy 

演示:

In [29]: goodguys = [Foo(0,"foo"), Foo(1,"bar"), Foo(2,"foobar"), Foo(-1,"barf")] 

In [30]: goodguys[:] = check_human_deaths(goodguys) 
foo has died... 
barf has died... 

In [31]: 

In [31]: print(goodguys) 
[<__main__.Foo instance at 0x7fdab2c74dd0>, <__main__.Foo instance at 0x7fdab2c6e908>] 

這個answer有一個非常好的解釋,爲什麼你不應該改變你正在迭代它的列表。

此外,如果你打算在列表和使用刪除年底開始,你可以使用逆轉而不是創建列表的另一個副本:

for guy in reversed(goodguys): 
     if guy.hp <= 0: 
      print('{} has died...'.format(guy.name)) 
      goodguys.remove(guy) 

但是,這不會是與第一種選擇一樣有效。