PR
【教育Python入門】変数の基本とメモリ参照などちょっと詳しい話
2022年度から高校で本格的に始まる「情報I」。2025年度から共通テストでも情報が科目として課されます。
この「教育Python入門」は、プログラミングを教える人にも、学ぶ人にも有用な、教育向けのプログラミングについて解説していきます。
最初は、プログラミングの基本、変数の扱いについて説明します。
言語はPythonを使って解説していきます。「Pythonを使える環境がないよ!」という人はこちらをご覧ください。
- Pythonの変数について、何を知っていればいいの?
- 変数をコピーしたときのメモリ参照の意味がイマイチ掴めないんだけど…
変数を定義して値を代入
変数の定義と値代入は簡単で、
変数名 = 値
と書くだけです。
a = 1
ちなみにプログラミングの「=」は「代入」を表しますので注意してください。
変数の型について
変数には「型」が存在します。Pythonの楽ちんなところは、型を宣言しなくていいところです。
この変数はこの型ですよ、と宣言する言語が多いですね。
型については後ほど詳しく説明しますが、高校の授業レベルであれば次の3つを理解していれば大丈夫だと思います。
- 数値: 数です。四則演算とかするアレです。
- 文字列: 「今日はいい天気です。」みたいなヤツです。Pythonでは”今日はいい天気です。”のように「””」で囲ってやります。
- リスト(配列): 複数の変数をセットにしたものです。「[]」で囲ってやって、「,」で区切ってやります。
num = 1
str = "今日はいい天気です。"
int_list = [0, 1, 2] #数値のリスト
name_list = ["たろう", "花子", "先生"] #文字列のリスト
mix_list = [0, 1, 2, "たろう", "花子", "先生"] #数値と文字列のリスト。型を混ぜることも可能。
リストの要素を取り出すには(list名)[n番目]で指定できます。nは0スタートですので注意してください。
name = name_list[0] #nameにname_listの0番目、つまり"たろう"が代入される
これ以外であれば辞書型(連想配列)くらいは知っていてもいいかもしれません。辞書型については後述します。
変数の注意点
命名規則
変数名として使える文字は次の3種類です。(漢字等でも使えるものもありますが、環境によっては使えない場合もあるのでやめておいた方が無難です。)
- 英字(大文字、小文字)
- 数字
- _(アンダースコア)
ただし、先頭に数字は使えません。エラーになります。
「_」(アンダースコア)は結構慣例的な使い方をする場合が多いです。単語を繋いだ変数を定義するときによく使います。が、変数の先頭につけたり、末尾につけたりしたときは、特別な意味をもつ変数になります。
あと、決まりはないのですが、慣例的に変数は小文字で書きます。
Pythonの変数を小文字で書くのは暗黙の了解ってやつですね。
予約語
プログラミングには、言語毎に「予約語」と言われる特別な意味をもつ単語があります。
例えば「for」や「if」です。
これらはプログラミングの動作に関係する単語なので、変数名には使えません。気を付けておいてください。
‘False’, ‘None’, ‘True’, ‘and’, ‘as’, ‘assert’, ‘async’, ‘await’, ‘break’,’class’, ‘continue’, ‘def’, ‘del’, ‘elif’, ‘else’, ‘except’, ‘finally’, ‘for’,’from’, ‘global’, ‘if’, ‘import’, ‘in’, ‘is’, ‘lambda’, ‘nonlocal’, ‘not’, ‘or’, ‘pass’, ‘raise’, ‘return’, ‘try’, ‘while’, ‘with’, ‘yield’
練習
それでは、変数についての練習問題をいくつか出題します。どんな結果になるか考えながら、Google Colabで実際にプログラミングをして確認してみましょう!
問1.次のプログラミングで、どのような値が出力されるでしょうか?(print(変数)で「変数の値を出力」します。)
num1 = 1
num2 = 2
sum = num1 + num2
print(sum)
問2.次のプログラミングで、どのような値が出力されるでしょうか?
greeting = "おはようございます。"
print(greeting)
greeting = "こんにちは。"
print(greeting)
問3.次のプログラミングで、どのような値が出力されるでしょうか?
today = "今日は、"
sunny = "晴れです。"
rain = "雨です。"
todays_weather = today + rain #文字列 + 文字列で文字列の連結ができる
print(todays_weather)
問4.次のプログラミングで、どのような値が出力されるでしょうか?
name_list = ["たろう", "花子", "先生"]
age_list = [16, 17, 28]
print(name_list[2])
print(age_list[2])
問5.次のプログラミングで、最後の結果を出力するためには(ア)に何を入力するべきでしょう?
name_list = ["たろう", "花子", "先生"]
age_list = ["16", "17", "28"]
index = (ア)
text = name_list[index] + "は" + age_list[index] + "才です。"
print(text)
#(出力) 花子は17才です。
問5は考えながら実際にプログラミングしてみましょう。indexには数字が入ります。
できれば知っておいて欲しいこと
ここでは、できれば知っておいて欲しいなぁと思うことを書いておきます!
辞書型について
辞書型とは「キーと値をセット」にして定義する配列です。
キーと値をセット?
要は「この値(キー)について、対応する値はこれですよ」という構造を定義したリストです。便利なので、使えそうなときには使った方がいいです。
例えば次のように使います。
age_dict = {"たろう": "16", "花子": "17", "先生": "28"}
#{}で囲って、(キー):(対応する値)をセットにして、リストのように,で区切ります。
name = "たろう"
text = name + "は" + age_dict[name] + "才です。"
print(text)
「別にリストでもいいんじゃない?」と思うかもしれませんが、リストから値を取得するためにはリストの何番目か?を把握しておかなければなりません。
一方、辞書型ではキーを指定すればそれに対応する値を取得することができます。
同じ挙動を実現するコードでも、色々な書き方があります。後々わかりにくくならない(=エラーが起きない)ようにするために、様々な書き方を試して「こういうときはこういう書き方がいいかな?」と考えていきましょう。
型について
Pythonは型について、そこまで気を配らなくても大丈夫な言語です。
が、型を意識することは重要です。
例.先ほどの問4のリストで問5と同じようなことをしてみます。
name_list = ["たろう", "花子", "先生"]
age_list = [16, 17, 28]
text = name_list[0] + "は" + age_list[0] + "才です。"
「たろうは16才です」という出力結果が得られそうなところですが、怒られます(エラーが出ます)。
TypeError: can only concatenate str (not "int") to str #全文ではないですが、こんなエラーが出ます。
これは、name_listは””で囲った「文字列」、age_listは「数値」のリストだからです。
ですので、「文字列」として+を使って連結しようとしたので「型が違うよ!」と怒られたのです。
このようなときは、文字列に変換するstr()を使いましょう。
text = str(name_list[0]) + "は" + str(age_list[0]) + "才です。"
このように、型を意識することは重要です。
Pythonは、型を無視してガンガン値を代入することができます。ただ、これははっきり言って危険行為です。
a = "たろう" #文字列を代入
print(a + "は")
a = 16 #同じ変数に数値を代入
print(a)
print("才です。")
ちなみにこんな無茶をしてもエラーは出ません。
ただ、先ほどの例のように、型によるエラーが起きる可能性があるのに、どんな型を入れているかわからなくなるようなプログラミングはしてはいけません。
ちょっと詳しい話
最後に、変数の扱いに関してちょっと詳しい話をしておきます。変数をコピーするときにちょっと意識しておいた方がいいよ、という話です。
差し当たって必要はないと思いますが、リストをコピーして要素を色々いじったりしていると、ここを意識する必要がある場面も出てくるかもしれません。
変数をコピーする際の注意点(メモリ参照の話)
さて、では次のような場面を想像してみましょう。
よし、俺(クマ)と兄の年齢をリストで準備しておいて(今クマ→5才、兄→7才)、今の年齢と5年後の年齢を出力するプログラミングを作ってみよう!
age_list = [5, 7]
after_5years = age_list #after_5yearsに今の年齢age_listをコピー
after_5years[0] += 5 #注
after_5years[1] += 5
print("クマの今の年齢は" + str(age_list[0]) + "、兄の今の年齢は" + str(age_list[1]))
print("クマの5年後の年齢は" + str(after_5years[0]) + "、兄の5年後の年齢は" + str(after_5years[1]))
注 「+=」はその変数に値を加えて代入、の省略。after_5years[0] = after_5years[0] + 5と同じ意味。
年齢のリストをコピーして、それぞれに5を加えて…。二つのリストを使って文字列を出力…完璧!
(出力結果)
クマの今の年齢は10、兄の今の年齢は12 #←ここは今の年齢が出て欲しい
クマの5年後の年齢は10、兄の5年後の年齢は12
なぜじゃあ…。
これは、リストを扱うときのあるある現象です。
ポイントは変数をコピーをした場合は「参照渡し」と「値代入」の違いをしっかりと意識しないといけない、ということです。
Pythonでは変数のコピーは「参照渡し」でコピーされます。
「参照渡し」とは、「この値はメモリ上のここを指す」という参照を渡す、ということです。
そして、値代入をすると、新たにメモリが割り当てられて値を代入し、参照がそのメモリの位置に変わります。
では、ここで以下のコードについて考えてみましょう。
str_a = "たろう"
str_b = str_a
str_b = "花子"
print(a)
print(b)
(出力結果)
たろう
花子
感覚的には当然な感じですが、このコードを細かく見ると、次のような流れになっています。
では、次にこのようなコードを考えてみましょう。
list_a = ["たろう", "花子"]
list_b = list_a
list_b[0] = "先生"
print(list_a)
print(list_b)
(出力結果)
["先生", "花子"]
["先生", "花子"]
イメージ的には、list_bの「たろう」を「先生」にしたいのですが、list_aの「たろう」も「先生」になってしまいました。
さて、ここで重要なことはリストや辞書式の変数は、メモリの参照先一覧を持っている、ということです。
ですので、list_bの「たろう」を「先生」に変更すると、次のような流れでメモリの参照先が変更されます。
ということで、list_aもlist_bの変更先を参照することになってしまいます。先ほどの5年後の年齢を出すコードもこの現象が起きています。
じゃあどうすればいいんだ…。
シャローコピー(浅いコピー)と呼ばれる方法をとれば大丈夫です。この方法は、メモリの参照先一覧を別にコピーする、というものです。
方法としては、
- リストや辞書式に実装されているcopy()メソッド(操作)を使う。
- スライスを使う。
といったものがあります。
list_b = list_a.copy()
#メソッドは、変数の後に.(メソッド)の形で記述する。とりあえず「その変数に対して決まった操作をするんだ」と思ってくれればOKです。
list_b = list_a[:]
#スライスは本来リストや文字列などに対して「ここからここまで切り取って代入」といった操作をします。ここでは「[:]で全て切り取っている」と思ってくれればOKです。
他にもcopyモジュールを使う、などもありますが、モジュールの扱いまで説明すると長くなるのでここでは割愛です。
先ほどの年齢のコードも以下のように修正すれば大丈夫です。
age_list = [5, 7]
after_5years = age_list.copy() #after_5yearsに今の年齢age_listをシャローコピー!
after_5years[0] += 5
after_5years[1] += 5
print("クマの今の年齢は" + str(age_list[0]) + "、兄の今の年齢は" + str(age_list[1]))
print("クマの5年後の年齢は" + str(after_5years[0]) + "、兄の5年後の年齢は" + str(after_5years[1]))
(出力結果)
クマの今の年齢は5、兄の今の年齢は7
クマの5年後の年齢は10、兄の5年後の年齢は12
やった!できたぞ!
まとめ
変数の基本と言いましたが、最後は結構難しい話までしてしまいました…。
とりあえず「練習」のところまで理解できれば基本は大丈夫です。
それ以降は、もう少しプログラミングの勉強を進めてから戻って読み直してもいいかもしれません。
次もプログラミングの基本である「繰り返し」を制御するfor文について説明していきます。