研究でpythonを書いていますが、for文の扱いでハマりました。
やりたいことは、リスト内のそれぞれのリストにある要素の順番を入れ替えるというもの。簡単な処理なのですが、これで困ったことが起こりました。for文内でリストの要素に処理を行っても元のリストの要素が書き換えられないのです。そういうわけで色々調べたので結果をまとめておきます。
1. テストコード
やりたいこと
リスト内にあるそれぞれ4つの要素を持つリストの0~1番目と2~3番目の要素を入れ替える
コード
list1 = [[0, 0, 1, 1], [2, 2, 3, 3]]
list2 = [[0, 0, 1, 1], [2, 2, 3, 3]]
for element in list1:
element = element[2:]+element[:2]
print(element)
for index, element in enumerate(list2):
list2[index] = element[2:]+element[:2]
print(list1)
print(list2)
出力結果
// for文内での結果
[1, 1, 0, 0]
[3, 3, 2, 2]
//for文外での結果
[[0, 0, 1, 1][2, 2, 3, 3]]
//適切な書き方をしたときの結果
[[1, 1, 0, 0][2, 2, 3, 3]]
2. やるべきこと
- リスト内の要素を書き換えたい場合は、添字[]を用いて代入してあげる必要がある。
- そのためには各要素のindexが必要である。
- このときに、enumerate()関数を用いることでリストの要素とインデックスを取得することができる。
3. その他の対処法
処理後のオブジェクトを別のオブジェクトに代入して、その新しいオブジェクトで元のオブジェクト内を書き換える。
list = [[0, 0, 1, 1], [2, 2, 3, 3]]
newList = []
for element in list:
newList.append(element[2:]+element[:2])
list = newList[:]
print(list) # 出力結果:[[1, 1, 0, 0], [3, 3, 2, 2]]
4. 理由
Pythonのレファレンスを参照します。
for 文は、シーケンス (文字列、タプルまたはリスト) や、その他の反復可能なオブジェクト (iterable object) 内の要素に渡って反復処理を行うために使われます:
for_stmt ::= "for" target_list "in" expression_list ":" suite ["else" ":" suite]
式リストは一度だけ評価されます。その結果はイテラブルオブジェクトにならなければなりません。 expression_list の結果に対するイテレータが生成されます。 その後、イテレータが与えるそれぞれの要素に対して、イテレータから返された順に一度づつ、スイートが実行されます。 それぞれの要素は標準の代入規則 (代入文 (assignment statement) を参照してください) で target_list に代入され、その後、スイートが実行されます。 全ての要素を使い切ったとき (シーケンスが空であったり、イテレータが StopIteration 例外を送出したときは、即座に)、 else 節があればそれが実行され、ループは終了します。
最初のスイートの中で break 文が実行されると、 else 節のスイートを実行することなくループを終了します。 continue 文が最初のスイート内で実行されると、スイート内にある残りの文の実行をスキップして、次の要素の処理に移るか、これ以上次の要素が無い場合は else 節の処理に移ります。
for ループはターゲットリスト内の変数への代入を行います。 これにより、for ループ内も含めて、それ以前の全ての代入は上書きされます:
for文内で処理されるのは、リストから取り出したtarget_listと言うリストです。この時、target_listは元のリストと同じ場所を参照します。しかし、このtarget_listに何らかの処理を加えた時、新しいリストが作成されるため、元のリストの場所を参照せず、新しいリストの場所を参照するようになります。
これを確かめるために次のようなコードを用意します。
list = [[0, 0, 1, 1], [2, 2, 3, 3]]
print("list's id = {}".format(id(list)))
print("list[0]'s id = {}".format(id(list[0])))
print("list[1]'s id = {}".format(id(list[1])))
print("--------------------------------------------") # 結果を見やすくするため
for element in list:
print("element's id = {}".format(id(element)))
element = element[2:]+element[:2]
print("Processed element's id = {}".format(id(element)))
print("--------------------------------------------") # 結果を見やすくするため
出力結果は次の通りになりました。
list's id = 2600570508224
list[0]'s id = 2600570507776
list[1]'s id = 2600567694080
--------------------------------------------
element's id = 2600570507776 # list[0]と同じ
Processed element's id = 2600570507648 # list[0]と異なる
--------------------------------------------
element's id = 2600567694080 # list[1]と同じ
Processed element's id = 2600570500160 # list[1]と異なる
以上の理由により、for文内で各要素に処理を行っても元のリストの要素を書き換えることができなかったようです。
5. おわりに
Pythonのfor文には気をつけよう!!