【Python初心者入門】初心者殺しの『値渡し・参照渡し』徹底解説

スポンサーリンク

はじめに

プログラミング初学者のかたがよく挫折するポイントの一つが『値渡し』と『参照渡し』の違いを理解できないことです。

これは本当によくあることで、プログラムの裏側の処理をイメージできていないことが要因かなと思っています。

今回は例え話などを交えて初学者の方でもわかりやすいように解説していきたいと思います!(イメージの話なので、正確なメモリの話ではありません。)

今回の事例はPythonを元にお話ししますが、RubyやJavaScriptなどの言語でも同じことです。是非Pythonの言語を学習中の方でなくても見ていってください。

クマくん
クマくん

実はよく分かっていないんだよね。

さめさん
さめさん

大丈夫。安心して!

徹底解説していくから!

そもそもの復習

本題に入る前に前提の知識を確認しておきましょう

データ型の確認

他のプログラミング言語と同様にPythonにはコード1つ1つに定められた型があります。

いつも使っているだろうデータ型はこんな感じでしょうか?

  • str型(文字列)
  • int型(整数)
  • float型(浮動小数点数)
  • list型(配列)
  • dict型(辞書)
  • set型(集合)
  • date型(日付)

などなど。

もちろん他にもたくさんありますが、Pythonのコードは全て〜〜型のデータとして存在しているのです。

# 文字列
string = "kumasan"

# データ型を確認する
type(string)
>>><class 'str'>

# 数字
sum = 5 + 6
type(sum)
>>><class 'int'>

# date型を確認する
# datetimeをインポートする
from datetime import date

# 今日の日付を変数に入れる 
today = date.today()
type(today)

# datetimeモジュールのdate型のデータである。
>>><class datetime.date>
さめさん
さめさん

Pythonでなんのデータ型か確認したいときは

type()メソッドを使おう!

リテラルって知ってる?

また、リテラルについても学習しておきましょう!

コンピュータプログラミング言語においてリテラルは、ソースコード内にを直接表記したものをいう。言語によってリテラルとして表記できるの種類や表記方法は異なる。簡略に記述できることが好まれるスクリプティング言語はリテラルの種類が多い傾向にある。リテラル表記ができるかどうかはその型が第一級オブジェクトかどうかを検討する材料である。静的に構文解析が可能なことが多いためシンタックスハイライトではたいてい色分けされる。なお言語によっては(標準規格等で)このリテラルのことを指して「定数」という用語を使っている場合もあるが、「リテラル」という語と使い分ける場合は、「定数」とは「初期化できるだけで、その後は値を変えられない変数」というようなものを指す。

リテラル
出典: フリー百科事典『ウィキペディア(Wikipedia)』

難しいことを言っているように読めますが、リテラルとは第1級オブジェクトであると言うことなんですね。

ここで第1級オブジェクトとはなんぞやってことなんですが、

第一級オブジェクトファーストクラスオブジェクト、first-class object)は、あるプログラミング言語において、たとえば生成、代入、演算、(引数戻り値としての)受け渡しといったその言語における基本的な操作を制限なしに使用できる対象のことである。ここで「オブジェクト」とは広く対象物・客体を意味し、必ずしもオブジェクト指向プログラミングにおけるオブジェクトを意味しない。第一級オブジェクトは「第一級データ型に属す」という。

第一級オブジェクト
出典: フリー百科事典『ウィキペディア(Wikipedia)』

これまた難しい言葉で定義されていますが、

初心者の方が理解するとしたら

リテラルとは文字列とか、数字とかの単体で存在できるもの

と言った覚え方がいいかもしれません。

単体で存在できるってどう言うこと?

実はプログラミングの世界は常に身分証明書(データ型の証明)を提示しないと生きられない世界なんです。

常にサイバーポリスが回っていて、『お前は何型のデータなんだ!?!?』って聞いてくるわけですね。

その時に、『私は文字列型のデータが入った変数です!』とか『私は関数型のデータで今呼び出されたので行ってきます!』

と言ったように自分がどのような存在なのか言えないといけません。

しかし、例えばデータが入っていない変数が生み出された時、

変数は『わ、私は。。。なんなんでしょうね・・・?』と言う状態なので、サイバーポリスは『逮捕する!!!』ってことで捕まえてエラーを表示させてしまいます。

つまり、単体で存在できるものとそうでないものがいる訳なんです。

値渡しと参照渡しの違い

さてここまでの前提知識を踏まえて、値渡しと参照渡しを説明していきます。

ここからはメモリ番地と言う概念が出てきます。

メモリ番地とは?

PCは情報を格納したり、処理を行うための土地を持っています。

いわゆるメモリです。

メモリには情報を入れておくための家を建っています。またはプログラムを走らせたり、アプリを起動するための工場なども建っているのです。

メモリには番地が割り振られており、情報を取り出す時にはメモリ番地にアクセスして取得します。

リテラルの番地の割り振りられ方

単体で存在できるリテラルはそれぞれのメモリ番地に格納されていきます。

値渡し

値渡しとはリテラルの中でも、文字列型や数字型、日付型などの値を渡すことを指します。

変数という箱に入った値渡し系のリテラルは同じメモリ番地に入ったリテラルを渡します。

今回変数aと文字列を格納し、変数bに入れた時に同じメモリ番地を参照しているか確認してみます。

# 変数aに文字列型を格納する
a = 'string'

# 変数bに変数aを格納する。
b = a

# その際にメモリ番地1の'string'がbに渡される。
# idメソッドでメモリ番地を確認し、同一か確かめてみる。
id(b) == id(a)
>>>True

これは下の図の通り、中に入っている値をそのまま渡しているからなんですね。

例え、変数bに入っている値を数字型のデータ(2番地に格納されるとする)で上書きしたとしても、変数aには1番地のメモリのデータが残ります。

クマくん
クマくん

ここまではイメージ通りだな。

さめさん
さめさん

いいね!

リスト型や辞書型などのリテラル

リスト型や辞書型は参照渡し系のリテラルです。

具体的な値の格納手順を見てみましょう。

['string']

本体の中に、さらにその中身ごとの管理している箱 (今回はサメさんですが)がいるのがわかると思います。リスト型や辞書型は中に複数の値を格納するためのリテラルなのでそういった中間管理職が用意されている訳ですね。

参照渡し

では早速、参照渡しがどういう手順に行われるのか確認してみましょう。

# 変数aにリスト型のデータを格納する
a = ['string']

# 変数bに変数aに入っているリスト型のデータを格納する
b = a

# idメソッドを使ってメモリ番地が一緒であることを確認する。
id(a) = id(b)
>>> True

変数aと変数bに入っている値は同じメモリ番地であることが確認できましたね。

変数bのリストの中の値だけ変更したい

それではやってみましょう!

変数bの中の値を変更したいので、以下のように始めてしまう人が多いと思います。

# bのリスト型のインデックス[0]にアクセスして置き換える。
b[0] = 'woman'

# 変数aと変数bの中身を確認する。
# どちらの値も変わっている。
print(a)
>>> ['woman']
print(b)
>>> ['woman']

このような現象がどうして起きてしまうか解説します。

まず、メモリ番地への登録の流れは以下のようになっています。

先ほど確認したように変数aと変数bは同じ1番地のメモリを参照しています。

変数bの値を更新した時、まず新しいデータは6番地のメモリに格納されます。そして、2番地のリストの中間管理職は3番地のデータを捨てて6番地のデータと結ばれてしまいます。

そして、どこからも参照されなくなった3番地のデータはサイバーポリス(ガベージコレクションと呼びます。)に逮捕され、メモリ番地から退去させられます。

その結果、本来変数aに残しておきたかった3番地のメモリのデータが無くなってしまうのです。

じゃあどうしたらいいの?

結論、元のデータをコピーします!

具体的な流れをまずはコード上で確認しましょう。

# 変数aにリスト型のデータを格納する
a = ['string']

# 変数bに変数aのコピーを格納する
b = a.copy()

# bのインデックス番号0番の値を更新
b[0] = 'woman'

# 変数a,bの中身が違っていることを確認
print(a)
>>>['string']

print(b)
>>>['woman']

次にメモリ登録のイメージを押さえましょう。

まず、変数aにリスト型のデータを格納しますが、変数bに代入するのは変数aのコピーです。

変数aとは別のメモリ番地に変数a’のような形で格納されます。

次に変数a’の中身もコピーされてものがそれぞれメモリ番地に格納されていきます。(実際は中身の文字列型から評価されるはずなので、順番が逆ですが一旦置いておきます。)

そして新しいデータが登場し、空いているメモリ番地に格納され、変数bのリストと紐付きます。

変数bは以前と異なり7番地のメモリを参照しているため、それに連なる8,9番地が参照されます。

リストの中間管理職である9番地のメモリは10番地のメモリの値を解放し、6番地のデータと紐付きます。

クマくん
クマくん

おおお!なんとなくわかった気がする!!

さめさん
さめさん

よかった!

さめさん
さめさん

正確な格納順じゃなかったり、一部抜けているところがあるかもしれないけど。イメージとしてはこんな感じ!

終わりに

値渡しと参照渡し
  • 参照渡しはリスト型や辞書型のデータ
  • 値渡しはそれ以外
  • 参照渡しをするときはコピーをしてから代入する
リスト型や辞書型のデータを扱うときは注意しよう!

Pythonの学習に関しては様々本屋には書籍が並んでいますが、下記がおすすめです。

EXCELなども使ったことのない初学者の方

EXCELなどでちょっとPCの操作に慣れている方

他のプログラミング言語を少し触ったことがある方

タイトルとURLをコピーしました