2016-02-29 43 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) 

但是,这不会是与第一种选择一样有效。