【问题标题】:Removing a class from a list [duplicate]从列表中删除一个类[重复]
【发布时间】:2016-06-13 04:01:07
【问题描述】:

我正在尝试根据其 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

我不确定如何将死者从战场上移走。任何提示表示赞赏。让我知道我是否以根本错误的方式处理此问题。

【问题讨论】:

标签: python list class python-3.x


【解决方案1】:

您收到此错误是因为您正在迭代列表的长度,比如说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 for 循环不会表现得异常

【讨论】:

  • 你是对的。我没有检查list() 是执行对象的深层复制还是只复制引用以及global 的可能问题goodguys,但我的最后一点是不正确的。
  • 只是为了纠正你,list() 不执行 deep copy,而是执行 shallow copy。列表中的类对象仍将保持不变。 list() 等价于 copy.copy(),而对于 python 中的深拷贝,我们有 copy.deepcopy()(它甚至会在列表中创建类对象的副本)。
  • 更少的更正和更多的澄清。谢谢 :) 以及对整个问题的可靠回答。
  • 我做到了。 :) 现在只有反对者也必须修改他的。
【解决方案2】:

问题是当你从 goodguys 中删除时,索引会减一。例如:

1,2,3,4

删除 2

1,3,4

3 的索引减一,大小减一。

【讨论】:

    【解决方案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]
    

    【讨论】:

    • lst[:] = [...] 会是更好的选择
    • 抱歉,我现在很忙——怎么回事?
    • 效率更高,尽管 OP 可能希望随时打印,但在这种情况下,for i in reversed(lst) 至少会避免复制。
    • @PadraicCunningham 我还是不明白是什么让lst[:] = [..] 更有效率?在此先感谢:)
    • @MichalFrystacky,每次你删除一个元素时,列表元素都会移动,因此 OP 的问题以及为什么改变你正在迭代的列表是一个坏主意,使用 lst[:] = [..] 类似于使用列表。删除,因为它会更改原始对象,同时保持列表组合的效率。
    【解决方案4】:

    正如其他人所说,您不能在迭代列表时更改列表,因为会发生奇怪的事情。相反,您可以保存要删除的元素并在循环后删除它们:

    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]
    

    【讨论】:

      【解决方案5】:
      goodguys = [ guy for guy in goodguys if guy.hp > 0 ]
      

      这将过滤掉阵列中所有死去的好人。

      你可以像这样在你的函数中使用它:

      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 ]
      

      【讨论】:

        【解决方案6】:

        您可以使用 生成器函数 来实现,从而让您以更有效和正确的方式打印和更新列表,只需遍历列表即可:

        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 很好地解释了为什么你不应该改变你正在迭代的列表。

        此外,如果您要从列表末尾开始并使用 remove,您可以使用 reversed 而不是创建列表的另一个副本:

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

        但这不会像第一个选项那样有效。

        【讨论】:

          猜你喜欢
          • 2020-03-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-10-23
          • 2018-05-16
          • 2017-09-28
          相关资源
          最近更新 更多