web-dev-qa-db-ja.com

Python 2.7 throws ValueError:list.remove(x):x not list

このプログラムを実行するたびに、次のエラーが発生します。

_ValueError: list.remove(x): x not in list
_

エイリアンがボルトに当たったときはいつでも、エイリアンの体力を下げようとしています。その単一のエイリアンも、そのヘルスが_<= 0_の場合は破棄する必要があります。同様に、ボルトも破壊されます。これが私のコードです:

_def manage_collide(bolts, aliens):
    # Check if a bolt collides with any alien(s)
    for b in bolts:
        for a in aliens:
            if b['rect'].colliderect(a['rect']):
                for a in aliens:
                    a['health'] -= 1
                    bolts.remove(b)
                    if a['health'] == 0:
                        aliens.remove(a)
    # Return bolts, aliens dictionaries
    return bolts, aliens
_

ValueErroraliens.remove(a)の行で発生します。明確にするために、aliensboltsは両方とも辞書のリストです。

何が悪いのですか?

14
Remolten

ループしているリストから項目を削除しないでください。代わりにコピーを作成します。

for a in aliens[:]:

そして

for b in bolts[:]:

ループ中にリストを変更すると、ループに影響します。

>>> lst = [1, 2, 3]
>>> for i in lst:
...     print i
...     lst.remove(i)
... 
1
3
>>> lst
[2]

2回ループしているリストから項目を削除すると、処理が少し複雑になり、ValueErrorが発生します。

>>> lst = [1, 2, 3]
>>> for i in lst:
...     for a in lst:
...         print i, a, lst
...         lst.remove(i)
... 
1 1 [1, 2, 3]
1 3 [2, 3]
Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
ValueError: list.remove(x): x not in list

ループのeachレベルで変更するリストのコピーを作成すると、問題を回避できます。

>>> lst = [1, 2, 3]
>>> for i in lst[:]:
...     for i in lst[:]:
...         print i, lst
...         lst.remove(i)
... 
1 [1, 2, 3]
2 [2, 3]
3 [3]

衝突が発生した場合は、bボルトonceを削除するだけで済みます。エイリアンを傷つけるループでは必要ありません。後でエイリアンを個別に一掃してください:

def manage_collide(bolts, aliens):
    for b in bolts[:]:
        for a in aliens:
            if b['rect'].colliderect(a['rect']) and a['health'] > 0:
                bolts.remove(b)
                for a in aliens:
                    a['health'] -= 1
    for a in aliens[:]:
        if a['health'] <= 0:
            aliens.remove(a)
    return bolts, aliens
20
Martijn Pieters

これを引き起こしているコードにバグがあります。簡略化したコードは次のようになります。

for b in bolts:
  for a in aliens:
    for a in aliens:
      bolts.remove(b)

これにより、aliensのすべてのエントリに対してbを複数回ループすることになります。 aliensの最初のループでbが削除された場合、2回目にループすると、エラーが発生します。

修正するいくつかのこと。まず、aliens以外の何かを使用するようにaの内部ループを変更します。

for b in bolts:
  for a in aliens:
    for c in aliens:
      if hit:
        bolts.remove(b)

次に、bboltsから一度だけ削除します。そう:

for b in bolts:
  for a in aliens:
    should_remove = False
    for c in aliens:
      if hit:
        should_remove = True
    if should_remove:
      bolts.remove(b)

このコードには他にも問題があると思いますが、それが主な問題の原因です。 Martijnの投稿も役立つかもしれません。

2
dave mankoff

ボルトにも「健康」を与え、1に初期化します。次に、1つのネストされたループを実行してすべての損傷を計算し、2つの別々のネストされていない「ループ」を実行して、「死んだ」ものをすべて削除します。ただし、ループしているリストを変更したくないので、そのようにしないでください。コピーを作成することはまだ複雑すぎます。あなたが本当にやりたいことは直接まだ「生きている」ものだけの新しいリストを作成するであり、リスト内包表記(またはここに示すようにfilter )。

# for example
class Alien:
    # ... other stuff
    def damage(self): self.hp -= 1
    def alive(self): return self.hp > 0

# similarly for Bolt

def collide(an_alien, a_bolt):
    # etc.

def handle_collisions(aliens, bolts):
    for a in aliens:
        for b in bolts:
            if collide(a, b):
                a.damage()
                b.damage()

    return list(filter(Alien.alive, aliens)), list(filter(Bolt.alive, bolts))
0
Karl Knechtel