「x = 5 と書いたとき、Pythonの内部では何が起きているの?」「なぜPythonでは変数の型を宣言しなくていいの?」――Pythonを学び始めると、なんとなく動くけれど仕組みがよくわからないまま使ってしまいがちなのが「変数」だ。
この記事では、Pythonの土台となる オブジェクト・型・ミュータビリティ・変数・代入 の考え方を、図解のかわりに具体例を使ってやさしく解説する。「変数は名前であって場所ではない」というPython特有の重要な考え方も扱う。
本記事は書籍『入門Python3 第2版』2章「データ:型、値、変数、そして名前」を参考にしつつ、Python公式ドキュメント(Python 3系の安定版)の内容に沿って解説している。
この記事でわかること
- Pythonのデータはすべて「オブジェクト」として扱われること
- 型(type)とは何か、基本データ型にはどんなものがあるか
- ミュータブル(変更可能)とイミュータブル(変更不可)の違い
- リテラル値と変数の違い
- 変数名として使える文字と、使ってはいけない予約語
=による代入がどのように行われるか- 「変数は名前であって場所ではない」という重要な考え方
- 複数代入・再代入・コピーをしたときの挙動の違い
1. Pythonのデータはすべて「オブジェクト」
Pythonでは、整数や文字列、リストなど、プログラムが扱うデータはすべて オブジェクト として扱われる。1つのオブジェクトは、次のような情報を持つデータのかたまりだ。
- そのデータで何ができるかを定義する 型
- ほかのオブジェクトと区別するための ID
- 型に従って解釈される 値
コンピュータのメモリから見れば、どんなデータも単なるビットの並びにすぎない。同じビットパターンでも、それを整数の65として扱うか、文字のAとして扱うかは「型」が決めている。Pythonでは、この「型」や「値」をオブジェクトという殻で包んで管理しているわけだ。
オブジェクトの型を調べるには、組み込み関数の type() を使う。
>>> type(7)
<class 'int'>
>>> type(3.14)
<class 'float'>
>>> type("abc")
<class 'str'>
>>> type([1, 2, 3])
<class 'list'>
<class 'int'> は「intクラスのインスタンスである」という意味だ。Pythonでは「型」と「クラス」は同じ意味なので、type(7) の結果は「7はint型(intクラス)のオブジェクトだ」と読み替えられる。クラスについて詳しくはオブジェクト指向を扱う章で説明するが、ここでは「型 = クラス」とだけ覚えておけば十分だ。
2. 型(type)を知る:基本データ型ひとまとめ
Pythonの基本的な組み込みデータ型を表にまとめると、次のようになる。
| 名前 | 型 | ミュータブル? | リテラルの例 |
|---|---|---|---|
| ブール値 | bool | × | True, False |
| 整数 | int | × | 47, 25_000 |
| 浮動小数点数 | float | × | 3.14, 2.7e5 |
| 複素数 | complex | × | 3j, 5 + 9j |
| 文字列 | str | × | 'alas', "alack" |
| リスト | list | ○ | ['a', 'b', 'c'] |
| タプル | tuple | × | (2, 4, 8) |
| バイト | bytes | × | b'ab\xff' |
| バイト配列 | bytearray | ○ | bytearray(...) |
| 集合 | set | ○ | {3, 5, 7} |
| frozenset | frozenset | × | frozenset([1, 2]) |
| 辞書 | dict | ○ | {'a': 1, 'b': 2} |
bool・int・floatといった数値型について詳しくは、以前の記事「数値型(int・float・bool)の基本をやさしく解説」もあわせて参照してほしい。この記事では、これらの型に共通する「変数」と「代入」の考え方に絞って説明していく。
3. ミュータビリティ:値を変更できる型・できない型
上の表の「ミュータブル?」の列は、そのオブジェクトを作成したあとで値を書き換えられるかどうかを示している。
- イミュータブル(immutable):値を変更できない型。int・float・str・tuple・frozensetなど。中身は見えるが書き換えられない「透明な窓付きの密閉された箱」のイメージだ。
- ミュータブル(mutable):値を変更できる型。list・dict・set・bytearrayなど。「蓋を開けて中身を書き換えられる箱」のイメージだ。
ただし、値がミュータブルであっても、オブジェクトの型そのものを変えることはできない。listの中身を書き換えてもlistのままだし、intをfloatに「その場で」変換することもできない。このように、Pythonは値がミュータブルかどうかにかかわらず型が変わらない「強い型付け(strong typing)」の言語だ。この違いは、あとで説明する「変数は名前であって場所ではない」という話と深く関わってくる。
4. リテラル値と変数
Pythonでデータの値を指定する方法は、大きく分けて2つある。
- リテラル:
5や"hello"のように、値そのものをソースコードに直接書く方法 - 変数:値に名前をつけて、あとから参照できるようにする方法
たとえば整数は数字の並び、浮動小数点数は小数点を含む数字の並び、文字列はクォートで囲まれた文字の並びとして、それぞれリテラルで表現できる。これらのリテラルに名前をつけたものが、次に説明する「変数」だ。
5. 変数の作り方:命名規則
変数とは、プログラムで使いたいメモリ上の値に名前を付けたものだ。Pythonの変数名には、次のような規則がある。
- 使える文字は、小文字英字(a〜z)・大文字英字(A〜Z)・数字(0〜9)・アンダースコア(_) のみ
- 大文字と小文字は区別される(
thing・Thing・THINGはすべて別の名前) - 先頭の文字は英字かアンダースコアでなければならない(数字から始めることはできない)
- 先頭がアンダースコアの名前は特別な意味を持つ場合がある(モジュール内限定の利用を示す慣習など。詳しくはオブジェクトとクラスを扱う章で説明する)
- Pythonの予約語(キーワード)であってはならない
有効な変数名の例:
a
a1
a_b_c___95
_abc
_1a
無効な変数名の例(数字から始まっている、末尾のアンダースコアが単体で意味を持たない、記号を含む、など):
1
1a
1_
name!
5.1 予約語(キーワード)とソフトキーワード
Python公式ドキュメントによると、Python 3系の予約語(キーワード)は次の35個だ。これらは変数名として使うことができない。
False await else import pass
None break except in raise
True class finally is return
and continue for lambda try
as def from nonlocal while
assert del global not with
async elif if or yield
Pythonプログラム内では、次のようにすれば予約語の一覧を確認できる。
>>> import keyword
>>> keyword.kwlist
また、Python 3.10でmatch・case・_が、Python 3.12でtypeが「ソフトキーワード」として追加された。ソフトキーワードは、match文やtype文といった特定の構文の中でのみ予約語として扱われ、それ以外の場所では通常の変数名として使えるという、少し変わった位置づけの名前だ。ソフトキーワードの一覧は keyword.softkwlist で確認できる。
予約語を変数名に使おうとすると、
SyntaxError: invalid syntaxが発生する。たとえばclass = "math"のようなコードはエラーになるので注意しよう。
6. 代入(=)の仕組み
Pythonでは = を使って変数に値を代入する。数学の =(等しい)とは意味が異なり、プログラムでの = は「右辺の値を左辺の変数に代入する」という意味だ。
>>> x = 5
>>> y = x + 12
>>> y
17
y = x + 12 という行を実行するとき、Pythonは次のような処理をしている。
- 中央に
=があることに気づく - これは代入だと判断する
- 右辺を計算する(
xが指すオブジェクトの値を取り出し、12を加える) - 計算結果を左辺の変数
yに代入する
= の右辺には、リテラル値かすでに値が代入済みの変数(またはそれらの組み合わせ)が必要だ。まだ値を持たない変数を使おうとすると、Pythonは例外(エラー)を送出する。
>>> y = x + 12
Traceback (most recent call last):
File "", line 1, in
NameError: name 'x' is not defined
7. 変数は名前であって場所ではない
ここがPython変数の最も重要なポイントだ。Pythonの変数はただの「名前」であり、値そのものを格納する「箱」ではない。
他の多くのプログラミング言語では、変数自体が型を持ち、メモリ上の決まった位置に値を書き込む。しかしPythonでは、名前はメモリ内のどこかにあるオブジェクトを指し示す「タグ」のようなもので、どんな型のオブジェクトでも指すことができる。値と型を知りたければ、その名前がどのオブジェクトを指しているかを「たどって」いけばよいのだ。
>>> a = 7
>>> b = a
>>> type(a)
<class 'int'>
>>> type(b)
<class 'int'>
変数が特定の型のオブジェクトを指しているかどうかを調べたいときは、isinstance() が使える。
>>> isinstance(7, int)
True
この「名前は参照にすぎない」という考え方が、次の複数代入・再代入・コピーの挙動を理解するカギになる。
8. 複数代入・再代入・コピーで気をつけたいこと
8.1 複数の名前への代入
同時に複数の変数へ同じ値を代入できる。
>>> two = deux = zwei = 2
>>> two
2
>>> deux
2
>>> zwei
2
8.2 名前への再代入
名前はオブジェクトを指しているだけなので、名前に別の値を代入すると、その名前は新しいオブジェクトを指すように変わるだけだ。もとのオブジェクトの値そのものが書き換えられるわけではない。
>>> x = 5
>>> y = x
>>> x = 29
>>> x
29
>>> y
5
y = x の時点で、yはxと同じ「値が5のオブジェクト」を指すようになる。その後 x = 29 としても、xが指す先が「値が29の新しいオブジェクト」に変わるだけで、yは変わらず5のオブジェクトを指したままだ。
8.3 ミュータブルなオブジェクトを共有するときの注意
ここが初心者がつまずきやすいポイントだ。イミュータブルなオブジェクト(int・str・tupleなど)なら上のように安心して使えるが、ミュータブルなオブジェクト(listなど)を複数の名前で共有している場合、片方の名前を通じて値を書き換えると、もう片方の名前で見える値も変わってしまう。
>>> a = [2, 4, 6]
>>> b = a
>>> a[0] = 99
>>> a
[99, 4, 6]
>>> b
[99, 4, 6]
b = a は値をコピーしているのではなく、aと同じリストオブジェクトにbという別の名前を貼り付けているだけだ。そのため、aを通じてリストの中身を書き換えると、同じオブジェクトを指しているbから見た値も変わる。値を独立させたコピーが必要な場合は、list()やcopy()、copy.deepcopy()などを使う必要がある(リストの章で詳しく扱う)。
9. 適切な変数名の選び方
変数に正しい名前を付けることは、見た目以上に重要だ。学習中はaやxのような使い捨ての名前を使うことも多いが、実際のプログラムでは同時に管理する変数の数がずっと多くなるため、簡潔さとわかりやすさのバランスを意識する必要がある。
たとえば、number_of_loonsよりもnum_loonsのほうが早くタイピングできるが、nよりもnum_loonsのほうが何を表しているかが一目でわかる。Pythonでは、単語をアンダースコアでつなぐsnake_case(例:user_name、total_count)が一般的な命名スタイルだ。
10. よくあるミスと注意点
① 予約語を変数名に使ってしまう
classやTrueのような予約語は変数名にできない。SyntaxErrorが出た場合は、予約語を使っていないか確認しよう。
② =(代入)と==(比較)を混同する
=は代入、==は値が等しいかどうかを調べる比較演算子だ。if x = 5:のように書くとSyntaxErrorになる。
③ ミュータブルなオブジェクトのコピーのつもりが「同じもの」になっている
b = aはリストなどのミュータブルなオブジェクトを複製しない。片方を書き換えるつもりがなくても、もう片方に影響が及ぶことがある。独立したコピーが必要ならlist(a)などを使うとよい。
④ 未初期化の変数を使ってしまう
値を代入する前に変数を使うとNameErrorになる。=の右辺で使う変数は、あらかじめ値が代入されている必要がある。
11. 練習問題
書籍2章の復習課題を参考にした、対話モードで試せる練習問題だ。
問題1:変数princeに値99を代入し、表示してみよう。
問題2:値5の型は何か。
問題3:値2.0の型は何か。
問題4:式5 + 2.0の型は何か。
解答例を見る
# 問題1
prince = 99
print(prince) # 99
# 問題2
print(type(5)) # <class 'int'>
# 問題3
print(type(2.0)) # <class 'float'>
# 問題4
print(type(5 + 2.0)) # <class 'float'>
問題4のように、intとfloatを混ぜて計算すると、結果は自動的にfloatになる。
まとめ
- Pythonのデータはすべて「オブジェクト」として扱われ、型・ID・値を持つ
- 型にはイミュータブル(int・str・tupleなど)とミュータブル(list・dict・setなど)がある
- 変数名に使えるのは英数字とアンダースコアのみで、数字から始めることはできず、予約語も使えない
=は「代入」を意味し、Pythonの変数は値そのものではなく、オブジェクトを指す「名前」である- 複数代入・再代入は名前の指し先が変わるだけだが、ミュータブルなオブジェクトを複数の名前で共有していると、片方の変更がもう片方にも影響する
次回以降は、この考え方を土台にして、条件分岐やループ、リスト・辞書といった具体的なデータ構造を扱っていく(数値型については、すでに公開した「数値型(int・float・bool)の基本をやさしく解説」もあわせて参照してほしい)。
参考資料
- 『入門Python3 第2版』Bill Lubanovic 著、鈴木駿 監訳、長尾高弘 訳(オライリー・ジャパン) 2章「データ:型、値、変数、そして名前」
- Python 公式ドキュメント:組み込み型
- Python 公式ドキュメント:字句解析 --- キーワード
- Python 公式ドキュメント:keyword --- Python キーワードのテスト
- Python 公式ドキュメント:単純文 --- 代入文