3

これは、Python でプログラミングを学ぼうとしているときに直面する最も一般的な問題です。問題は、「range()」関数を使用してリストを反復して、リスト内の特定の項目が特定の条件を満たしているかどうかを確認し、そうであればそれを削除しようとすると、常に「IndexError」が発生することです。それで、他の中間リストまたは「while」ステートメントを使用せずにこれを行う特定の方法はありますか? 以下に例を示します。

l = range(20)
for i in range(0,len(l)):
  if l[i] == something:
    l.pop(i)
4

6 に答える 6

7

まず第一に、Python ではそのようなことを反復したくありません。インデックスではなく、実際のオブジェクトを反復処理します。

l = range(20)
for i in l:
    ...

エラーの理由は、アイテムを削除していたため、それ以降のインデックスが存在しなくなったためです。

現在、リストをループしている間はリストを変更できませんが、それは問題ではありません。より良い解決策は、ここでリスト内包表記を使用して、余分な項目を除外することです。

l = range(20)
new_l = [i for i in l if not i == something]

ビルトインを使用するfilter()こともできますが、ほとんどの状況で不明確になる傾向があります (必要な場合は遅くなりますlambda)。

range()また、Python 3.x では、リストではなくジェネレーターを生成することに注意してください。

また、よりわかりやすい変数名を使用することをお勧めします。ここでは例として想定しますが、iやのような名前lは読みにくく、バグが入りやすくなります。

編集:

コメントで指摘されているように、既存のリストをその場で更新する場合は、スライシング構文を使用して、リストの各項目を順番に置き換えることができます ( l[:] = new_l)。そうは言っても、そのケースはかなり悪いデザインだと私は主張します. コードの 1 つのセグメントが、そのように別のコードから更新されるデータに依存することは望ましくありません。

編集2:

何らかの理由で、アイテムをループするときにインデックスが必要な場合は、それがビルトインの目的enumerate()です

于 2012-05-10T17:47:15.477 に答える
2

リスト内包表記を使用すると、いつでもこの種のことができます。

newlist=[i for i in oldlist if not condition ]
于 2012-05-10T17:47:44.057 に答える
0

を取得する理由IndexErrorは、forループで反復するときにリストの長さを変更しているためです。基本的に、ここにロジックがあります...

#-- Build the original list: [0, 1, 2, ..., 19]
l = range(20)

#-- Here, the range function builds ANOTHER list, in this case also [0, 1, 2, ..., 19]
#-- the variable "i" will be bound to each element of this list, so i = 0 (loop), then i = 1 (loop), i = 2, etc.
for i in range(0,len(l)):
    if i == something:
        #-- So, when i is equivalent to something, you "pop" the list, l.
        #-- the length of l is now *19* elements, NOT 20 (you just removed one)
        l.pop(i)
    #-- So...when the list has been shortened to 19 elements...
    #-- we're still iterating, i = 17 (loop), i = 18 (loop), i = 19 *CRASH*
    #-- There is no 19th element of l, as l (after you popped out an element) only
    #-- has indices 0, ..., 18, now.

また、リストのインデックス付きセルにあるものではなく、リストのインデックスに基づいて「ポップ」の決定を行っていることにも注意してください。これは珍しいことです-それはあなたの意図でしたか?それとももっと似たような意味でしたか...

if l[i] == something:
    l.pop(i)

さて、あなたの特定の例では(l[i] == i)、これは典型的なパターンではありません。

リストを反復処理するのではなく、フィルター関数を試してください。これは組み込みです(他の多くのリスト処理機能のように:たとえば、マップ、ソート、リバース、zipなど)

これを試して...

#-- Create a function for testing the elements of the list.
def f(x):
    if (x == SOMETHING):
        return False
    else:
        return True

#-- Create the original list.
l = range(20)

#-- Apply the function f to each element of l.
#-- Where f(l[i]) is True, the element l[i] is kept and will be in the new list, m.
#-- Where f(l[i]) is False, the element l[i] is passed over and will NOT appear in m.
m = filter(f, l)

リスト処理関数は「ラムダ」関数と連携します。これは、Pythonでは簡単な無名関数です。したがって、上記のコードを次のように書き直すことができます...

#-- Create the original list.
l = range(20)

#-- Apply the function f to each element of l.
#-- Where lambda is True, the element l[i] is kept and will be in the new list, m.
#-- Where lambda is False, the element l[i] is passed over and will NOT appear in m.
m = filter(lambda x: (x != SOMETHING), l)

それを試してみて、それがどのように機能するかを見てください!

于 2012-05-10T18:02:40.827 に答える
0

反対側から問題を見る必要があります。要素が「何か」と等しい場合にリストに要素を追加します。リスト内包表記:

l = [i for i in xrange(20) if i != something]
于 2012-05-10T17:48:58.933 に答える
0
  • を使用しないでください。インデックスが必要な場合は代わりに使用しfor i in range(0,len(l)):、そうでない場合は使用してくださいfor i, item in enumerate(l):for item in l:
  • 繰り返している構造を操作しないでください。そうすることに直面したときは、代わりにコピーを繰り返します
  • 変数に l という名前を付けないでください (1 または I と間違えられる可能性があります)
  • リストをフィルタリングする場合は、明示的に行ってください。内包表記を使用filter()またはリストする

ところで、あなたの場合、次のこともできます。

while something in list_: list_.remove(something)

しかし、それはあまり効率的ではありません。しかし、文脈によっては、より読みやすいかもしれません。

于 2012-05-10T17:50:06.750 に答える
0

他の人が言ったように、リストを繰り返して、保持したいアイテムだけで新しいリストを作成します.

スライスの割り当てを使用して、元のリストをその場で更新します。

l[:] = [item for item in l if item != something]
于 2012-05-10T17:52:44.740 に答える