3

このトピックに関するほとんどすべてのチュートリアルとSOの回答では、リストを繰り返し処理している間はリストを変更しないでくださいと主張していますが、コードが有効である場合、これがなぜこれほど悪いことなのかわかりません。例えば:

while len(mylist) > 0:
    print mylist.pop()

私は何かが足りないのですか?

4

4 に答える 4

13
while len(mylist) > 0:
    print mylist.pop()

リストを繰り返し処理していません。あなたは毎回原子の状態をチェックしています。

また:

while len(mylist) > 0:

次のように書き直すことができます。

while len(mylist):

これは次のように書き直すことができます。

while mylist:
于 2012-05-30T08:02:24.617 に答える
9

リストを繰り返し処理するときにリストを変更しない理由は、たとえば、20桁のリストを繰り返し処理し、偶数をヒットした場合はリストからポップして、リストが作成されるまで続行するためです。ちょうど奇数の。

さて、これがあなたのサンプルデータ[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]であると言って、あなたはそれを繰り返し始めます。最初の繰り返し、そして番号は1あなたが続けるように、次の番号は2あなたがそれを飛び出して、すすぎそして繰り返すためのものです。結果のリストがであるため、アプリケーションが正しく機能したと感じます[1, 3, 5, 7, 9, 11, 13, 15, 17, 19]

ここで、サンプルデータが[1, 2, 4, 5, 7, 8, 10, 11, 12, 13, 15, 15, 17, 18, 20]であり、前と同じコードを実行し、元のリストを反復処理しながら変更するとします。結果のリストは[1, 4, 5, 7, 10, 11, 13, 15, 15, 17, 20]、リストにまだ偶数が含まれているため、明らかに正しくありません。

リストを繰り返しながら変更することを計画している場合は、

for elem in lst:
    # mutate list in place

むしろ変更する必要があります

for elem in lst[:]:
    # mutate list in place

構文により、元のリストの正確なコピーである新しいリストが作成されます。これ[:]により、処理対象に影響を与えることなく、元のリストを変更しても意図しない副作用が発生しないため、元のリストを変更できます。繰り返します。

リストのサイズがかなり大きい場合は、新しいリストを作成してステップスルーする代わりに、ジェネレーター式を使用するか、必要に応じてリスト用に独自のジェネレーターを作成して、メモリとCPUサイクルを無駄にしないようにします。

于 2012-05-30T08:16:28.410 に答える
7

リストを繰り返さない理由についてもう少し詳しく説明します。当然、つまり

for elt in my_list:
    my_list.pop()

または同様のイディオム。

forまず、Pythonのループが何をするかを考える必要があります。任意のオブジェクトを反復処理することができるため、Pythonは、指定したオブジェクトを反復処理する方法を必ずしも認識していません。したがって、値を1つずつ表示する方法を理解するために実行しようとすることのリスト(heh)があります。そして、最初に行うことは__iter__、オブジェクトのメソッドをチェックし、存在する場合はそれを呼び出すことです。

この呼び出しの結果は、反復可能なオブジェクトになります。つまり、nextメソッドを持つものです。nextこれで準備は完了です。が発生するまで繰り返し呼び出しますStopIteration

何でこれが大切ですか?メソッド__iter__は実際にデータ構造を調べて値を見つけ、次にどこを見ればよいかがわかるように内部状態を覚えておく必要があるためです。ただし、データ構造を変更すると__iter__、いじっていることを知る方法がないため、新しいデータを取得しようとし続けます。これが実際に意味することは、おそらくリストの要素をスキップするということです。


ソースコードを見て、この種の主張を正当化することは常に素晴らしいことです。差出人listobject.c

static PyObject *
listiter_next(listiterobject *it)
{
    PyListObject *seq;
    PyObject *item;

    assert(it != NULL);
    seq = it->it_seq;
    if (seq == NULL)
        return NULL;
    assert(PyList_Check(seq));

    if (it->it_index < PyList_GET_SIZE(seq)) {
        item = PyList_GET_ITEM(seq, it->it_index);
        ++it->it_index;
        Py_INCREF(item);
        return item;
    }

    Py_DECREF(seq);
    it->it_seq = NULL;
    return NULL;
}

特に、インデックス変数の役割を果たして、Cスタイルのforループを実際にシミュレートしていることに注意してください。it->it_index特に、リストからアイテムを削除すると更新されないit_indexため、値をスキップできます。

于 2012-05-30T08:35:23.080 に答える
4

コードはリストを反復処理しません。

for i in mylist:
  print mylist.pop()
于 2012-05-30T08:02:50.017 に答える