2

この Python for ステートメントで少し時間を無駄にしました。

class MyListContainer:
    def __init__(self):
        self.list = []

    def purge(self):
        for object in self.list:
            if (object.my_cond()):
                self.list.remove(object)
        return self.list

container = MyListContainer()

# now suppose both obj.my_cond() return True
obj1 = MyCustomObject(par)
obj2 = MyCustomObject(other_par)

container.list = [obj1, obj2]

# returning not an empty list but [obj2]
container.purge()

「パージ」のサイクルがリストの最初のオブジェクトを削除すると、2番目のオブジェクトがリストの先頭に移動してサイクルが終了するため、期待どおりに機能しません。

forサイクルの前にself.listの複製を解決しました:

...
local_list = self.list[:]
for object in local_list:
...

元のリストの長さを変更しているため、 for ステートメントが機能しなくなったと思います。誰かがこの点を明確にすることができますか?

そして、この問題を解決するためのより「エレガントな」方法はありますか? リスト内に複数の要素がある場合、毎回複製するのは得策ではないようです。

おそらくfilter()関数が正しい関数ですが、もしあれば他のアプローチが必要です。

私は初心者です。


あなたの有用な答えを要約するには:

  • ループしているリストを編集しないでください
  • リストを複製するか、リスト内包表記を使用してください
  • リストを複製しても記憶が無駄になることはありません。
4

7 に答える 7

5

しようとしないでください。ただしないでください。コピーを作成するか、新しいリストを生成します。

于 2010-02-09T23:45:03.243 に答える
3

新しいリストを作成してください:

def purge(self):
    self.list = [object for object in self.list if not object.my_cond()]
    return self.list

プロファイリングを行い、この方法が実際にアプリケーションのボトルネックであることが判明するまで、最適化を保留してください。(そうではないに違いない。)

于 2010-02-09T23:45:41.127 に答える
2

Python では、変数は実際にはデータのラベルです。リストの複製とは、ほとんどの場合、最初のリストのデータへのポインターの新しいセットを作成することです。あまり気を悪くしないでください。

リスト内包表記はあなたの友達です。

例えば

>>> a = range(20)
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
>>> [ x for x in a if x % 2 == 0 ]
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
于 2010-02-09T23:53:06.550 に答える
2

フィルター (またはリスト内包表記) は進むべき道です。インプレースで実行する場合は、次のようにします。

purge = []
for i,object in enumerate(self.list):
    if object.mycond()
        purge.append(i)
for i in reversed(purge):
    del self.list[i]

または、代わりに、消去リストを理解して作成することもできます。ショートカット バージョンは次のようになります。

for i in reversed([ i for (i,o) in enumerate(self.list) if o.mycond() ]):
    del self.list[i]
于 2010-02-09T23:54:11.143 に答える
0

逆にリストを短縮するのは完全に安全です!

>>> a=range(20)
>>> for i in reversed(range(len(a))):
...     if a[i]%2: del a[i]
... 
>>> a
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

別の方法は、スライス全体を再割り当てすることです

>>> a=range(20)
>>> a[:]=(x for x in a if not x%2)
>>> a
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

リスト内のアイテムが一意の場合、これも機能します

>>> a=range(20)
>>> for item in reversed(a):
...  if item%2: a.remove(item)
... 
>>> a
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

yuri のコメントに応じて、さらに説明を加えます。

私たちが持っているとしましょう

>>> a=[0,1,2,3,4,5]

単純に3番目と4番目のアイテムを削除しようとしています

>>> del a[3]
>>> del a[4]
>>> a
[0, 1, 2, 4] # didn't work because the position of all the item with index >=3 was changed

ただしdel、逆の順序で 'sを実行すると

>>> a=[0,1,2,3,4,5]
>>> del a[4]
>>> del a[3]
>>> a
[0, 1, 2, 5] # this is the desired result

削除条件を使用して for ループにこのアイデアを拡張すると、ライブ リストからの削除が可能であることがわかります。

于 2010-02-10T00:13:18.383 に答える
0

リストを複製する 2 番目のソリューションは正しい方法です。その後、必要に応じて古いリストを複製に置き換えることができます。

于 2010-02-09T23:48:57.657 に答える
-1
indeces = []
minus = 0

for i in range(self.list):
    if cond(self.list[i]):
        indeces.append(i)

for i in indeces:
    self.list = self.list[:(i-minus)].extend(self.list[i-minus+1:])
于 2010-02-10T01:31:16.307 に答える