質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Q&A

解決済

2回答

84閲覧

多次元リストの一部の数値だけ変えたい

Sigma1630

総合スコア36

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

0グッド

0クリップ

投稿2018/06/01 11:54

リストの一部を変更させたいのに、余計なところまで変更されてしまいます。
その理由を教えてほしいです。

【入力】

Python

1a = [[0,0,0],[0,0,0],[0,0,0]] 2a[1][1]+=1 3print("a =",a) 4 5b = [[0]*3]*3 6b[1][1]+=1 7print("b =",b) 8

【期待する出力】

a = [[0, 0, 0], [0, 1, 0], [0, 0, 0]] b = [[0, 0, 0], [0, 1, 0], [0, 0, 0]] #どちらも同じ

【実際】

a = [[0, 0, 0], [0, 1, 0], [0, 0, 0]] b = [[0, 1, 0], [0, 1, 0], [0, 1, 0]] #bだけ余計なものまで1になっている

同じ[[0,0,0],[0,0,0],[0,0,0]]の真ん中を変更する作業なのに、bは余計なところまで
1に変更されています。なぜでしょうか。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

そもそもリストというのは、あえて言えば各要素をポインタで持っているデータ構造です。余計なところまで変わってしまう理由は、「同じオブジェクト(インスタンス)へのポインタになっている」からです。この書き方だとそうなります。

pythonのオブジェクトには固有のidがあり、組み込み関数id()で確認できます。

python

1>>> b = [[0]*3]*3 2>>> id(b[0]) 3139883677073800 4>>> id(b[1]) 5139883677073800 6>>> id(b[2]) 7139883677073800

ぜんぶ同じものを指しています。

初期化したければ、aの書き方でも良いですし、

python

1>>> c = [[0]*3 for _ in range(3)] 2>>> id(c[0]) 3139883677075976 4>>> id(c[1]) 5139883677075784 6>>> id(c[2]) 7139883677075656

これでも良いです([0]*3がまずいのでは? という疑問もあると思いますが、0はimmutableなオブジェクトなのでこれで基本的に問題ありません。immutableが何なのかは、ご自身で調べてみてください)。

これはpython界隈ではとても有名な話なので、検索するといろいろ出てきます。

参考:
Python のリストの扱いで注意すること

投稿2018/06/01 12:07

hayataka2049

総合スコア30933

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Sigma1630

2018/06/01 12:24

ありがとうございます。 >>[0]*3がまずいのでは? これ本当に思いました。同じオブジェクトを指してるならなんで[[1,1,1],[1,1,1],[1,1,1]]にならないんだろう、と。immutable, 調べてみます。
hayataka2049

2018/06/01 12:29 編集

実はそのへんの話も参考に貼った記事に書いてあったんですが・・・(今見返したら気づいた) pythonではint, str, tupleなどがimmutableなオブジェクトですが、これらは自分自身は絶対に変わらない、代入したら代入されたやつに置き換わるし、演算も他のやつ(演算結果のオブジェクト)を連れてきてそいつと入れ替わる、というのがミソです >>> i = 0 >>> id(i) 9169056 >>> i = 1 >>> id(i) # 0自身は変化せず、iに結びつくオブジェクトが1に変わるだけ 9169088
guest

0

後者は、リストの参照値が共有されてしまうからです。
Qiita - pythonでのリストの要素の変更とリストのコピー

良く躓くポイントなので、調べれば同様の話題はたくさん出てきます。


実際にidを確認してみると良いです。

Python

1a = [[0, 0, 0], [0, 0, 0], [0, 0, 0]] 2a[1][1] = 1 3 4for e in a: 5 print(f'{id(e)}: {e}') 6 7print('-'*42) 8 9# 10# 11b = [[0] * 3] * 3 12b[1][1] = 1 13 14for e in b: 15 print(f'{id(e)}: {e}') 16 17print('-'*42) 18 19# 20# 21c = [[0] * 3 for _ in range(3)] 22c[1][1] = 1 23 24for e in c: 25 print(f'{id(e)}: {e}') 26 27print('-'*42)

実行結果 Wandbox

139895659521672: [0, 0, 0] 139895659521864: [0, 1, 0] 139895659522184: [0, 0, 0] ------------------------------------------ 139895659523656: [0, 1, 0] 139895659523656: [0, 1, 0] 139895659523656: [0, 1, 0] ------------------------------------------ 139895659523976: [0, 0, 0] 139895659524040: [0, 1, 0] 139895659544648: [0, 0, 0] ------------------------------------------

投稿2018/06/01 11:58

編集2018/06/01 12:09
LouiS0616

総合スコア35660

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Sigma1630

2018/06/01 12:21

ありがとうございます。これもまた前教えて頂いた話と似てますね。=にせよ*にせよ、リストの複製は気をつける必要がありますね。
LouiS0616

2018/06/01 12:24

ありゃ、ほんとですね。 慣れていてもうっかりハマることがあるので、日頃から気を付けられると良いですね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問