forループ中にリストエントリを変更する方法は?


178

これで、繰り返しループ中にリストを変更するのは安全ではないことがわかりました。ただし、文字列のリストがあり、文字列自体を削除したいとします。変更可能な値の置換は変更としてカウントされますか?


20
文字列は変更可能な値を作成しません。
user470379 2010年

4
@ user470379:リストの要素が変更可能かどうかは、ループしているときにリストの要素を変更しても安全かどうかには関係ありません。
martineau 2013年

回答:


144

それは貧弱な形態と考えられています。代わりにリスト内包表記を使用してください。リストへの既存の参照を保持する必要がある場合は、スライス割り当てを使用してください。

a = [1, 3, 5]
b = a
a[:] = [x + 2 for x in a]
print(b)

10
スライスの割り当ては賢く、ループ中にオリジナルを変更することを避けますが、オリジナルの長さの一時リストを作成する必要があります。
martineau

11
なぜb = aを割り当てるのですか?
Vigrond 2013年

9
@Vigrond:したがって、print bステートメントが実行されると、a置き換えられたのではなく、インプレースで変更されたかどうかを確認できます。別の可能性はprint b is a、両方がまだ同じオブジェクトを参照しているかどうかを確認することでした。
martineau 2013年

12
なぜa [:] =であり、単なるa =ではないのですか?
kdubs 2017年

10
@kdubs: "...リストへの既存の参照を保持する必要がある場合は、スライスを割り当てます。"
Ignacio Vazquez-Abrams

162

以下のループはすでに見た要素を変更するだけなので、許容できると考えられます:

a = ['a',' b', 'c ', ' d ']

for i, s in enumerate(a):
    a[i] = s.strip()

print(a) # -> ['a', 'b', 'c', 'd']

これは以下とは異なります:

a[:] = [s.strip() for s in a]

一時的なリストの作成や、元のリストを置き換えるための一時的なリストの割り当ては必要ありませんが、より多くのインデックス操作が必要です。

注意:この方法でエントリを変更できますが、list問題が発生する可能性を危険にさらさずにのアイテム数を変更することはできません。

これが私の意味の例です—エントリを削除すると、その時点からインデックスが混乱します。

b = ['a', ' b', 'c ', ' d ']

for i, s in enumerate(b):
    if s.strip() != b[i]:  # leading or trailing whitespace?
        del b[i]

print(b)  # -> ['a', 'c ']  # WRONG!

(必要なアイテムがすべて削除されなかったため、結果は間違っています。)

更新

これはかなり人気のある回答なので、「インプレース」でエントリを効果的に削除する方法を次に示します(正確には問題ではありません)。

b = ['a',' b', 'c ', ' d ']

b[:] = [entry for entry in b if entry.strip() == entry]

print(b)  # -> ['a']  # CORRECT

3
Pythonが構文の個々の要素のコピーしか作成しないのはなぜfor i in aですか?これは非常に直感に反し、他の言語とは一見異なるように思われ、コードでエラーが発生し、長期間デバッグする必要がありました。Pythonチュートリアルはそれについてさえ言及していません。それには何らかの理由があるに違いありませんが?
xji

1
@JIXiang:コピーを作成しません。これは、ループ変数名を、反復されるものの連続する要素または値に割り当てるだけです。
martineau 2017年

1
必要ないのに、なぜ同じ行の同じオブジェクトに2つの名前(a[i]s)を使うのですか?むしろやりたいですa[i] = a[i].strip()
Navin

3
@Navin:a[i] = s.strip()インデックス作成操作は1つだけなので。
martineau 2017

1
@martineau enumerate(b)はすべての反復でインデックス作成操作を実行し、別の操作をで実行していますa[i] =。私の知る限り、ループの反復ごとに1つのインデックス付け操作だけでPythonにこのループを実装することは不可能です:(
Navin

18

forループのバリアントがもう1つあり、enumerate()を使用するよりもきれいに見えます。

for idx in range(len(list)):
    list[idx]=... # set a new value
    # some other code which doesn't let you use a list comprehension

19
多くの人はrange(len(list))、Pythonのようなコードの匂いを使用することを検討しています。
martineau 14

2
@Reishin:enumerateはジェネレーターなので、list.ofタプルを作成するのではなく、リストを反復処理するときに一度に1つずつ作成します。どちらが遅いかを知る唯一の方法は、となるでしょうtimeit
martineau

3
@martineauのコードはあまりきれいではないかもしれませんtimeit enumerateが、遅いとされています
Reishin

2
@Reishin:与えられたインデックスでリストの値を取得する必要性が考慮されていないため、ベンチマークコードは完全に有効ではありません。これもこの回答には示されていません。
martineau

4
@Reishin:そのため、比較は正確に無効です。ループのオーバーヘッドを個別に測定しています。結論として、特定の方法でループするループのループ内のコードに提供される利点によってオーバーヘッドの違いが軽減される可能性があるため、ループ全体の実行にかかる時間を測定する必要があります。それ以外の場合は、リンゴを比較しませんりんご。
martineau

11

リストへの要素の追加/削除を変更しない限り、リストの反復中に各要素を変更することは問題ありません。

リスト内包表記を使用できます。

l = ['a', ' list', 'of ', ' string ']
l = [item.strip() for item in l]

または単にC-styleforループを実行します。

for index, item in enumerate(l):
    l[index] = item.strip()

4

文字列をそのように変更できれば、リストの「コンテンツ」を変更することはできません。しかし、Pythonでは変更できません。文字列操作は、新しい文字列を返します。

変更可能であることがわかっているオブジェクトのリストがある場合は、リストの実際の内容を変更しない限り、これを行うことができます。

したがって、ある種のマップを作成する必要があります。ジェネレータ式を使用する場合、反復するにつれて[演算]が実行され、メモリが節約されます。


4

あなたはこのようなことをすることができます:

a = [1,2,3,4,5]
b = [i**2 for i in a]

これはリスト内包表記と呼ばれ、リスト内でループするのを容易にします。


3

Jemshit IskenderovとIgnacio Vazquez-Abramsの答えは本当に良いです。この例でさらに説明できます。

a)2つのベクトルのリストが提供されます。

b)リストを走査して、配列のそれぞれの順序を逆にしたい

あなたが持っているとしましょう

v = np.array([1, 2,3,4])
b = np.array([3,4,6])

for i in [v, b]:
    i = i[::-1]   # this command does not reverse the string

print([v,b])

あなたが得るでしょう

[array([1, 2, 3, 4]), array([3, 4, 6])]

一方、もし

v = np.array([1, 2,3,4])
b = np.array([3,4,6])

for i in [v, b]:
   i[:] = i[::-1]   # this command reverses the string

print([v,b])

結果は

[array([4, 3, 2, 1]), array([6, 4, 3])]

1

削除する文字列を決定するための基準が何であるかは質問からは明らかではありませんが、削除する文字列のリストを持っている、または作成できる場合は、次のことを行うことができます。

my_strings = ['a','b','c','d','e']
undesirable_strings = ['b','d']
for undesirable_string in undesirable_strings:
    for i in range(my_strings.count(undesirable_string)):
        my_strings.remove(undesirable_string)

my_stringsを['a'、 'c'、 'e']に変更します


0

要するに、同じリストを反復しながらリストに変更を加えることです。

list[:] = ["Modify the list" for each_element in list "Condition Check"]

例:

list[:] = [list.remove(each_element) for each_element in list if each_element in ["data1", "data2"]]
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.