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

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

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

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

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

2回答

617閲覧

配列のコピー元を変更するとコピー先の配列まで変更されるのはなぜか

SomeoneHello

総合スコア5

Python 3.x

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

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2021/11/09 14:08

python

1>>> list1 = ['A', 'B', 'C'] 2>>> list2 = ['1', '2'] 3>>> list1[1:] = list2 4>>> print(list1) # ['A', '1', '2'] 5>>> list2[0] = 'abc' 6>>> print(list1) # ['A', '1', '2']

python

1list1 = ['A', 'B', 'C'] 2list2 = [[1, 2], [3, 4]] 3list1[1:] = list2 4print(list1) # ['A', [1, 2], [3, 4]] 5list2[0][0] = 'abc' 6print(list1) # ['A', ['abc', 2], [3, 4]]

上の2つの状況は、配列の次元以外は同じだと思うのですが、上の状況では、コピー元であるlist2を変えてもコピー先であるlist1は変わっていないのに対し、下の状況ではなぜ変わってしまうのでしょうか?

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

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

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

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

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

guest

回答2

0

参照しているオブジェクト自体が変更されたからです.と言っても難しいと思うので,順に説明していきます.

ミュータブルとイミュータブル

Pythonのオブジェクトはイミュータブル(変更不可)かミュータブル(変更可)のどちらかです.
数値や文字列,タプルなどはイミュータブルです.

python

1s = "hoge" 2s[1] = "a" #=> エラー!

このように変更しようとするとエラーになります.
対してリストや辞書などはミュータブルです.

python

1l = ["h", "o", "g", "e"] 2l[1] = "a" #=> ['h', 'a', 'g', 'e']

エラー無く変更できますね.

代入

よく代入は「変数という箱にデータを入れる」と例えられますが,Pythonにおいては少し違います.
変数に入るのはデータ(オブジェクト)自体ではなく,その参照です.
参照をめちゃくちゃ簡単に言うと,「オブジェクトを指し示す矢印」です.

本題

次のプログラムは,質問者さんの「上の状況」を簡単にしたものです.

python

1a = 123 2b = a 3print(a) #=> 123 4print(b) #=> 123 5a = 987 6print(a) #=> 987 7print(b) #=> 123 8

このようなプログラムで何が起こっているかというと.

まず1行目で,123という数値オブジェクトが作成されて,これを指し示す矢印(参照)が変数aに入ります.

次に2行目で変数aに入っている参照が変数bに入ります.
つまり,123というオブジェクトはコピーされず1つのままで,その参照がコピーされているわけです.
なので,この時点では変数aも変数bも同じ1つのオブジェクトを参照しています.

最後に5行目で,987というオブジェクトが新たに作成され,その参照が変数aに入ります.
このとき,123というオブジェクト自体が変更されたわけではないので,変数bには123への参照が入ったままです.(そもそもイミュータブルだから変更できない)

次に「下の状況」を簡単にしたプログラムです.

python

1a = [1, 2, 3] 2b = a 3print(a) #=> [1, 2, 3] 4print(b) #=> [1, 2, 3] 5a[1] = 9 6print(a) #=> [1, 9, 3] 7print(b) #=> [1, 9, 3]

4行目までは同じですね.
リストオブジェクトを作成して,その参照を変数aと変数bに入れています.

5行目が異なります.
変数aが参照しているリストオブジェクトの要素を変更していますね.
リストはミュータブルなので問題なくオブジェクト自体を変更できます.
そして,変数bも同じオブジェクトを参照しているので,変数aと同じになります.

おしまい

ミュータブルなオブジェクトを代入したり関数の引数に渡したりするときは,間接的にオブジェクトの値が変更できてしまうので気をつけましょう.

投稿2021/11/09 18:13

karintly

総合スコア68

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

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

0

ベストアンサー

Python では変数とオブジェクトとの関係は束縛(bind)となっています。
全ての変数は、参照変数と考えると分かりやすいと思います。
さらに言えば、変数にはオブジェクトを参照する情報が格納されていると考えても矛盾はないと思います。
実際 CPython の実装では変数にオブジェクトのアドレスが格納されていると聞いています。

Python

1list1 = ['A', 'B', 'C'] 2list2 = ['1', '2'] 3list1[1:] = list2 4print(list1) # ['A', '1', '2']

list1[1] には list2[0] に格納されている '1' への参照情報を代入したので
list1[1] の id と list2[0] の id つまり参照している '1' の id は同じです。

Python

1print(id(list1[1])) # この id と 2print(id(list2[0])) # この id は同じ 3list2[0] = 'abc'

とすると list2[0] は 'abc' への参照情報で上書きされます。

Python

1print(list1) # ['A', '1', '2']

list1[1] には '1' への参照情報が入ったままなので '1' を参照したままです。

Python

1list1 = ['A', 'B', 'C'] 2list2 = [[1, 2], [3, 4]] 3list1[1:] = list2 4print(list1) # ['A', [1, 2], [3, 4]]

list1[1] には list2[0] に格納されている [1, 2] への参照情報を代入したので
list1[1] の id と list2[0] の id つまり参照している [1, 2] の id は同じです。

Python

1print(id(list1[1])) # この id と 2print(id(list2[0])) # この id は同じ 3list2[0][0] = 'abc'

とすると list2[0][0] は 'abc' への参照情報で上書きされますが、これは list2[0] が参照している [1, 2] の最初の要素を 1 の参照情報から 'abc' の参照情報に上書きしています。
list2[0] が参照しているリスト自体は元のままで、その最初の要素の参照情報が書き換わっただけです。
list2[0] と list1[1] は同じリストを参照したままですから

Python

1print(list1) # ['A', ['abc', 2], [3, 4]]

となります。

投稿2021/11/09 15:56

lehshell

総合スコア1156

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

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

退会済みユーザー

退会済みユーザー

2021/11/10 10:46

今回の質問はpythonの変数には値型がなく参照型しかないというだけで、ミュータブルかどうかは関係ないと思ったので、こちらを高評価しました(仮に値型のミュータブルがあったらややこしいだけ)。
lehshell

2021/11/11 14:44

そうですね。今回の質問にミュータブルかイミュータブルかは関係ありませんね。 実際、list2[0]がミュータブルでもlist2[0]に代入するのであれば以下のように影響しませんね。 list1 = ['A', 'B', 'C'] list2 = [[1, 2], [3, 4]] list1[1:] = list2 print(list1) # ['A', [1, 2], [3, 4]] list2[0] = 'abc' print(list1) # ['A', [1, 2], [3, 4]]
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問