846

Python でリストのリストを作成する必要があったため、次のように入力しました。

my_list = [[1] * 4] * 3

リストは次のようになりました。

[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]  

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

my_list[0][0] = 5

今、私のリストは次のようになります。

[[5, 1, 1, 1], [5, 1, 1, 1], [5, 1, 1, 1]]  

これは私が望んでいた、または期待したものではありません。誰かが何が起こっているのか、それを回避する方法を説明できますか?

4

17 に答える 17

718

書く[x]*3と、基本的に list が得られます[x, x, x]。つまり、同じ への参照が 3 つあるリストxです。次に、このシングルを変更すると、xそれへの 3 つの参照すべてを介して表示されます。

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

x[0] = 42
print(f"x: {x}")
# x: [42, 1, 1, 1]
print(f"l: {l}")
# l: [[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 = 21 を 2 に変えるようなことはできません。

于 2008-10-27T15:03:30.080 に答える
67

Actually, this is exactly what you would expect. Let's decompose what is happening here:

You write

lst = [[1] * 4] * 3

This is equivalent to:

lst1 = [1]*4
lst = [lst1]*3

This means lst is a list with 3 elements all pointing to lst1. This means the two following lines are equivalent:

lst[0][0] = 5
lst1[0] = 5

As lst[0] is nothing but lst1.

To obtain the desired behavior, you can use a list comprehension:

lst = [ [1]*4 for n in range(3) ]

In this case, the expression is re-evaluated for each n, leading to a different list.

于 2008-10-27T15:07:02.647 に答える
44
[[1] * 4] * 3

あるいは:

[[1, 1, 1, 1]] * 3

内部リストの 3 つのコピーではなく、内部を 3 回参照するリストを作成します。その[1,1,1,1]ため、リストを (任意の位置で) 変更するたびに、変更が 3 回表示されます。

次の例と同じです。

>>> inner = [1,1,1,1]
>>> outer = [inner]*3
>>> outer
[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
>>> inner[0] = 5
>>> outer
[[5, 1, 1, 1], [5, 1, 1, 1], [5, 1, 1, 1]]

それはおそらく少し驚くべきことではありません。

于 2008-10-27T15:02:54.310 に答える
10

次のコードを使用して要素が重複するリストを作成する代わりに、問題を正しく説明した受け入れられた回答と並んで:

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

また、itertools.repeat()繰り返し要素の反復子オブジェクトを作成するために使用できます。

>>> a = list(repeat(1,4))
[1, 1, 1, 1]
>>> a[0] = 5
>>> a
[5, 1, 1, 1]

PS NumPyを使用していて、使用できる1または0の配列のみを作成したい場合np.onesnp.zerosおよび/または他の数値を使用するにはnp.repeat:

>>> import numpy as np
>>> np.ones(4)
array([1., 1., 1., 1.])
>>> np.ones((4, 2))
array([[1., 1.],
       [1., 1.],
       [1., 1.],
       [1., 1.]])
>>> np.zeros((4, 2))
array([[0., 0.],
       [0., 0.],
       [0., 0.],
       [0., 0.]])
>>> np.repeat([7], 10)
array([7, 7, 7, 7, 7, 7, 7, 7, 7, 7])
于 2015-06-17T17:08:52.533 に答える
7

Python コンテナには、他のオブジェクトへの参照が含まれています。次の例を参照してください。

>>> a = []
>>> b = [a]
>>> b
[[]]
>>> a.append(1)
>>> b
[[1]]

これbは、 list への参照である 1 つの項目を含むリストaです。リストaは変更可能です。

リストを整数で乗算することは、リストをそれ自体に複数回追加することと同じです (一般的なシーケンス操作を参照してください)。例を続けます。

>>> c = b + b
>>> c
[[1], [1]]
>>>
>>> a[0] = 2
>>> c
[[2], [2]]

リストには、 と同等のclist への参照が 2 つ含まれていることがわかります。ac = b * 2

Python FAQ には、この動作の説明も含まれています:多次元リストを作成するにはどうすればよいですか?

于 2016-04-06T13:40:43.340 に答える
4

簡単に言えば、Pythonではすべてが参照によって機能するため、これが発生しているため、そのようにリストのリストを作成すると、基本的にそのような問題が発生します。

問題を解決するには、次のいずれかを実行できます。 1. numpy.empty に numpy 配列のドキュメントを使用 します。3.必要に応じて辞書を使用することもできます

于 2016-06-14T06:36:52.540 に答える
3

もっと分かりやすく説明しようとすると、

操作 1:

x = [[0, 0], [0, 0]]
print(type(x)) # <class 'list'>
print(x) # [[0, 0], [0, 0]]

x[0][0] = 1
print(x) # [[1, 0], [0, 0]]

操作 2:

y = [[0] * 2] * 2
print(type(y)) # <class 'list'>
print(y) # [[0, 0], [0, 0]]

y[0][0] = 1
print(y) # [[1, 0], [1, 0]]

最初のリストの最初の要素を変更しないと、各リストの 2 番目の要素が変更されないのはなぜですか? これ[0] * 2は、実際には 2 つの数値のリストであり、0 への参照を変更できないためです。

クローン コピーを作成する場合は、操作 3 を試してください。

import copy
y = [0] * 2   
print(y)   # [0, 0]

y = [y, copy.deepcopy(y)]  
print(y) # [[0, 0], [0, 0]]

y[0][0] = 1
print(y) # [[1, 0], [0, 0]]

クローン コピーを作成するもう 1 つの興味深い方法、操作 4:

import copy
y = [0] * 2
print(y) # [0, 0]

y = [copy.deepcopy(y) for num in range(1,5)]
print(y) # [[0, 0], [0, 0], [0, 0], [0, 0]]

y[0][0] = 5
print(y) # [[5, 0], [0, 0], [0, 0], [0, 0]]
于 2016-08-10T07:09:51.783 に答える
2

組み込みのリスト機能を使用すると、このようにすることができます

a
out:[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
#Displaying the list

a.remove(a[0])
out:[[1, 1, 1, 1], [1, 1, 1, 1]]
# Removed the first element of the list in which you want altered number

a.append([5,1,1,1])
out:[[1, 1, 1, 1], [1, 1, 1, 1], [5, 1, 1, 1]]
# append the element in the list but the appended element as you can see is appended in last but you want that in starting

a.reverse()
out:[[5, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
#So at last reverse the whole list to get the desired list
于 2016-07-15T13:48:36.223 に答える