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

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

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

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

Q&A

2回答

971閲覧

参照型とそうでない型で結果が異なるのはなぜか

taiyo2017

総合スコア170

Python

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

0グッド

0クリップ

投稿2018/11/13 14:07

def mutateA(val): val = val + 3 def mutateB(ref): ref[0] = ref[0] + 3 val = 6 ref = [6] mutateA(val) mutateB(ref) print(val) print(ref[0])

というコードがあったとします。この時、
print(val)は6になり、
print(ref[0])は9になります。
なぜそうなるのかわかりません。
僕は、print(val)もprint(ref[0])も9になると考えました。どちらも6+3をしていると考えたからです。
なぜprint(val)は6になるのでしょうか?
参照型とそうでない型の違いがよくわかっていません・・・。

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

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

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

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

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

guest

回答2

0

他の言語には「参照型」と「そうでない型」の区別があるのかもしれませんが、Pythonではある意味すべてが「参照型」みたいなものです。なので、あえて参照型と呼ぶことは通常しません。Rubyもそうですが、すべてがオブジェクトというのはそういうことです。

さて、質問文のコードのそれぞれの関数の中にある代入文は、一見するとどちらも「ただの代入文」っぽく見えます。が、実のところそれぞれまったく異なった処理が為されます。

  • val = val + 3

左辺のvalは単なる識別子なので、これが実行された名前空間上のvalという識別子に対してval + 3を評価した結果を束縛します。なお、val + 3の評価では通常新しいオブジェクトが返りますが、必須ではありません(新しいオブジェクトを返す設計になっているオブジェクトが圧倒的に多いというだけで、オブジェクトの__add__メソッドを自分自身を返すように実装することもできます)。

  • ref[0] = ref[0] + 3

左辺のref[0]は添字表記なので、まずrefが評価されて添字表記で代入できるオブジェクトかどうかが確認されます。問題なければ、次に添字の中身が評価されて、どの添字に代入するかを評価します。(ここからlistの場合やdictの場合、はたまた自作オブジェクトの場合など型によって処理が分かれる要素があります。)代入先が決まったら、ref[0] + 3を評価した結果を代入します。ここで実際には、左辺のオブジェクトの__setitem__リンク)というメソッドが呼ばれて(組み込み型だとメソッド呼び出しを短絡する最適化か何かがあるかもしれませんが)、オブジェクトの内部状態が書き換えられます。

この辺の仕様は以下に書いてあります(構文のリファレンスなので読むのはしんどいと思いますが)。

7. 単純文 (simple statement) — Python 3.6.5 ドキュメント 7.2. 代入文 (assignment statement)

要するに識別子に対して代入すると名前空間のシンボルテーブルを操作することになるのに対し、オブジェクトの添字表記に対して代入するとオブジェクトの値(具体的にはそのオブジェクト[その添字]が指すもの)を変えることになります。

前者の場合、ローカルの名前空間をいじっても呼び出し元に波及する要素はありませんが、後者の場合は呼び出し元と呼び出し先で引数のオブジェクトが共有されていますから(最初に書いた通り、整数だろうとリストだろうとオブジェクトそのものは参照により共有されるのです)、呼び出し元にもオブジェクトの値の変化という形で影響が波及します。

投稿2018/11/13 22:24

編集2018/11/14 04:27
hayataka2049

総合スコア30933

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

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

0

参照型とそうでない型の違い

まずここに誤解があります。Pythonの変数は、全て参照値を保持します
というか、変数が直接保持しているのは参照値だけです。

参照値が変更されない限り、全ての変更は呼び出し元に影響します。

Python

1>>> def func(lst): 2... print(id(lst), lst) 3... lst = [1, 2, 3] 4... print(id(lst), lst) 5... 6>>> l = [0, 0, 0] 7>>> 8>>> print(id(l), l) 92497531337096 [0, 0, 0] 10>>> 11>>> func(l) 122497531337096 [0, 0, 0] 132497531335560 [1, 2, 3] # 関数内で参照値が書き換わっているので、 14>>> 15>>> print(id(l), l) 162497531337096 [0, 0, 0] # 呼び出し元に影響しない

Python

1>>> def func(lst): 2... print(id(lst), lst) 3... lst[0] = 42 4... print(id(lst), lst) 5... 6>>> l = [0, 0, 0] 7>>> 8>>> print(id(l), l) 92497531335560 [0, 0, 0] 10>>> 11>>> func(l) 122497531335560 [0, 0, 0] 132497531335560 [42, 0, 0] # 参照値が書き換わらないので、 14>>> 15>>> print(id(l), l) 162497531335560 [42, 0, 0] # 呼び出し元にも影響する

参照値が書き換わるときとは、変数が新たなオブジェクトを指すようになるときです。

Python

1val = val + 6

この二つのvalは、名前は同じでも全くの別物だと考えた方が良いです。

投稿2018/11/13 14:16

編集2018/11/13 14:34
LouiS0616

総合スコア35660

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.47%

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

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

質問する

関連した質問