リストの変更が予期せずサブリスト全体に反映される 質問する

リストの変更が予期せずサブリスト全体に反映される 質問する

リストのリストを作成しました:

>>> xs = [[1] * 4] * 3
>>> print(xs)
[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]

次に、最も内側の値の 1 つを変更しました。

>>> xs[0][0] = 5
>>> print(xs)
[[5, 1, 1, 1], [5, 1, 1, 1], [5, 1, 1, 1]]

各サブリストの最初の要素が に変更されたのはなぜですか5?


参照:

ベストアンサー1

と記述すると[x]*3、基本的にリスト が得られます[x, x, x]。つまり、同じ への 3 つの参照を持つリストですx。この 1 つを変更すると、xへの 3 つの参照すべてを介して表示されます。

x = [1] * 4
xs = [x] * 3
print(f"id(x): {id(x)}")
# id(x): 140560897920048
print(
    f"id(xs[0]): {id(xs[0])}\n"
    f"id(xs[1]): {id(xs[1])}\n"
    f"id(xs[2]): {id(xs[2])}"
)
# id(xs[0]): 140560897920048
# id(xs[1]): 140560897920048
# id(xs[2]): 140560897920048

x[0] = 42
print(f"x: {x}")
# x: [42, 1, 1, 1]
print(f"xs: {xs}")
# xs: [[42, 1, 1, 1], [42, 1, 1, 1], [42, 1, 1, 1]]

これを修正するには、各位置に新しいリストを作成する必要があります。その方法の1つは次のとおりです。

[[1]*4 for _ in range(3)]

[1]*4一度評価して 1 つのリストを 3 回参照するのではなく、毎回再評価します。


*リスト内包表記のように、 が独立したオブジェクトを作成できないのはなぜかと疑問に思うかもしれません。これは、乗算演算子が*式を参照せずにオブジェクトに対して演算を行うためです。 を使用して3 を*乗算すると、は 1 要素のリストの評価結果のみを参照し、式テキストは参照しません。は、その要素のコピーを作成する方法、 を再評価する方法、さらにはコピーが必要であることすら認識しません。一般に、要素をコピーする方法がない場合もあります。[[1] * 4]*[[1] * 4][[1] * 4*[[1] * 4]

唯一の選択肢*は、新しいサブリストを作成するのではなく、既存のサブリストへの新しい参照を作成することです。それ以外の場合は、一貫性がなくなるか、基本的な言語設計の決定を大幅に再設計する必要があります。

対照的に、リストの内包表記は、反復ごとに要素式を再評価します。 は、同じ理由で毎回[[1] * 4 for n in range(3)]再評価されます。 の評価ごとに新しいリストが生成されるため、リストの内包表記は必要な処理を実行します。[1] * 4[x**2 for x in range(3)]x**2[1] * 4

ちなみに、[1] * 4も の要素をコピーしません[1]が、整数は不変なので、これは問題ではありません。 のような操作を行っ1.value = 2て 1 を 2 に変換することはできません。

おすすめ記事