0

私は誰かのためにいくつかのコードをデバッグしようとしていますが、かなり奇妙なシナリオに遭遇しました。コードの目的は、指定されたリストで重複を検索し、重複のないリストを返すことです。(注意してください、コードを書いている人は、私が個人的に新しいリストに各値を追加するだけなのに、リストから重複を単純に削除することを選択しました。しかし、私はまだ奇妙なことに興味をそそられています)。コードは次のとおりです。

def remove_duplicates(duplicates):
    duplicates_del = duplicates 
    for i in duplicates_del:
        if duplicates_del.count(i) > 1:
            duplicates_del.remove(i)
    return duplicates_del

remove_duplicates([3, 3, 3, 3, 3, 3])

実行すると、コードが返さ[3, 3, 3]れ、いくつかのデバッグの後、コードが 4 になるまで問題なく動作することがわかりましたduplicates_del.count(i)。次のラウンドでは、for ステートメント内のすべてを完全にスキップし、return ステートメントに直接移動します。その結果、得られる答えが得られます。

if ステートメントを に変更するwhile duplicates_del.count(i) > 1:と、コードが問題なく実行されることがわかりました。

デバッガーのコードを調べたところ、カウントを無視できるブレークポイント クラスがあることがわかりました。if ステートメントが何らかの形でこのブレークポイントをトリガーしているのでしょうか、それとも while ループの代わりに if ステートメントを使用するとコードが完全に実行されない別の理由がありますか?

4

2 に答える 2

2

これが発生する理由は、アイテムを削除しているときにリストを反復処理しているためです。これはほとんどの場合、予期しない結果になります。を見てみましょう:

L = [1, 2, 3, 4, 5]
for item in L:
    if item == 1 or item == 2 or item == 3:
        L.remove(item)
print L

出力は次のとおりです。

[2, 4, 5]

2削除されていないことに注意してください。item各ループで印刷すると、次のようになります。

1
3
5

Python が を削除した後1、リストの順序が変更され、2必ずしもループ内の次の項目になるとは限りません (実際に3はそうです)。4もスキップされることに注意してください。


このような動作を回避するには、リストのコピーを反復処理する必要があります。悲しいことに、あなたがしたことはコピーを作ることではありませんでした。実行duplicates_del = duplicatesすると、両方のオブジェクトが同じ ID を参照するようになるため、一方の要素を変更すると、他方の要素も変更されます。

これを行う必要があります:

def remove_duplicates(duplicates):
    for i in duplicates[:]: # Creates a copy of the list
        if duplicates.count(i) > 1:
            duplicates.remove(i)
    return duplicates
于 2013-11-07T03:12:21.453 に答える
1

リストをループすると、リストから削除されます。

通常、これは、削除された次の項目がスキップされることを意味します。

この場合remove、毎回最初に一致する要素が削除されるため、リスト全体が下にシフトされます。リスト反復子はリストが変更されたことを認識しないため、次の項目にインクリメントします。

于 2013-11-07T03:12:15.460 に答える