クラスの説明からは「初心者」から「初級者」にタイトルを変えることにします。 カッコ内の番号は通し番号にします。
プログラムをクラスという仕組みにまとめることができます。
例えば、電卓プログラムを作るとしましょう。
プログラムの中の次のような部分をまとめてクラスCalc
を作ります。
なお、Calc
はcalculator(電卓)の最初の4文字を取ったものです。
また、クラス名は大文字で始めることになっています。
- 電卓(のクラス
Calc
)を初期化して使える状態にする - 電卓で加減算の計算をする
- 計算結果を電卓の中に保持をする
ここでは、簡単な電卓プログラムを通じてクラスの基本を学びます。
クラス、インスタンス、インスタンス変数、メソッドとは?
クラス、インスタンス、インスタンス変数、メソッドはセットで理解するようにしましょう。
- クラスは設計図のようなもの。例えば電卓のクラスは、それが保持するデータや外部に提供する加減算のプログラムを定義している
- インスタンスはクラスをもとに作られる。これは、設計図をもとに実物を作るようなもの。例えば、電卓のクラスから、2つの「
calcA
」「calcB
」などのインスタンスを作ることができる。それぞれのインスタンスは独立して動作する - インスタンス変数はその名の通り、インスタンスに関連付けられた変数で、「インスタンス名.変数名」(インスタンス名+ドット+変数名)という形で参照される
- メソッドは、クラスに定義された関数で、個々の「インスタンス名.メソッド名(引数の並び)」(インスタンス名+ドット+メソッド名+カッコ内に引数の並び)の形で呼び出すことができる。これをメソッドという。例えば、電卓のクラスにおいて、足し算を
add
という名前の関数で定義していたとする。Calcクラスのインスタンスx
を通してx.add(3, 4)
の形で呼び出すことができる
クラス定義の基本
クラス定義にはclass
キーワードを使います。その基本構文は以下のとおりです。
class クラス名: def __init__(self, 引数1, 引数2, ...): # 初期化処理 ... ... ... def メソッド名(self, 引数1, 引数2, ...): # メソッドの処理 ... ... ...
この構文で、クラスの名前を定義し、その中に関数や変数をまとめることで、一つのまとまった設計図を作ることができます。
__init__
とは?
クラスの中で特別な名前の関数として__init__
があります。
これは「初期化関数」と呼ばれ、インスタンスが生成されるときに自動的に呼び出されます(インスタンスの生成方法は後ほど説明します)。
この関数を利用して、インスタンスの初期状態を設定できます。
関数とメソッドの違い
クラス定義内で定義された関数は、インスタンスを通じて呼び出されます。 このときは「関数」といわず「メソッド」と呼びます。 つまり、「メソッド」はインスタンスを通して呼ばれるクラス定義内の関数のことです。
クラス定義内の関数の第1引数は、この関数がメソッドとして呼ばれたときのインスタンスです。
慣例的にこれをself
と書くことにしています。
他の名前にすることは可能ですが、それはしないでください。
常にself
と書くことにより、プログラムを理解しやすくなります。
class Calc: ... ... ... ... ... ... def add(self, a, b): self.m = a + b return self.m
このプログラムでは、クラスCalc
の定義の中で関数add
を定義しています。
add
の第1引数のself
は、インスタンスを指し、それ以外に2つの引数a
とb
があります。
外部からこのメソッドを呼ぶためには、まずインスタンスの生成が必要です。
仮に生成されたインスタンスをx
とします。
すると、このメソッドはx.add(10, 20)
のような形で呼び出されます。
呼び出し時には、関数定義のa
とb
の部分だけが引数に書かれ、self
の部分は書きません。
self
はシステムが自動的に第一引数にして、関数add
を呼び出します。
インスタンス変数
インスタンス変数は、「インスタンス名.変数名」(インスタンス名+ドット+変数名)の形で参照される変数です。
仮に、Calcクラスのインスタンスを生成し、それを変数x
に代入したとします。
x.m
は、変数x
の指すインスタンスのインスタンス変数です。
クラス内の関数定義では第1引数のself
を使ってインスタンス変数を参照します。
さきほどの変数x
はクラスの外部で生成されていることに注意してください。
したがって、x
をクラスの内部で使うことはできません。
なお、self
は予約語(プログラム言語で使われるワードで、例えばdef, return, classなど)ではありません。
単に関数の第1引数を表す慣例的な変数です。
したがって、クラス定義の中の関数定義の外では使うことはできません。
インスタンス変数とクラス変数
クラス内では、インスタンスごとに異なる値を保持する「インスタンス変数」と、クラス全体で共有される「クラス変数」を定義できます。
以下の例では、クラス変数total_calculators
を追加して、作成された電卓インスタンスの数を記録します。
class Calc: total_calculators = 0 # クラス変数 def __init__(self, m=0): self.m = m # インスタンス変数 Calc.total_calculators += 1 # クラス変数を更新
このように、クラス変数はすべてのインスタンスで共有され、全体に関連するデータを扱うのに便利です。
クラス変数を外部から参照するときは「クラス名.変数名」(クラス名+ドット+変数名)を使います。
print(Calc.total_calculators) # クラス変数 total_calculators を表示する
クラス変数やインスタンス変数は、そのクラスやインスタンスの外部でも定義することができます。 なお、「定義」されるのは、最初に変数に代入をしたときです。 しかし、外部での定義は混乱をもたらす原因となるので、極力避けるべきです。 常に定義は内部で行い、外部からは定義済みの変数を読み書きするようにしましょう。
インスタンスの生成
クラス定義の後に、インスタンスを生成できます。 インスタンスの生成は、クラス名で関数呼び出しのようにすればできます。
x = Calc()
これで変数xにCalcクラスのインスタンスが代入されます。
クラスの例:Calcクラス(電卓のクラス)
以下は電卓クラスCalc
の定義例です。このクラスには次の機能があります。
- 記憶した値(メモリ)を持つ
- 加算、減算を行い、結果を記憶する
- 記憶した値をリセットする
- 初期値をインスタンス生成時に渡せる
class Calc: total_calculators = 0 def __init__(self, m=0): self.m = m Calc.total_calculators += 1 def add(self, a, b=None): if b is None: self.m += a else: self.m = a + b return self.m def sub(self, a, b=None): if b is None: self.m -= a else: self.m = a - b return self.m def reset(self): self.m = 0
total_calculators
はクラス変数。インスタンスを生成するごとに1だけ増え、インスタンスの個数を保持する__init__
関数はインスタンス生成時に自動的に呼び出される。インスタンスの初期化のための関数。2番目の引数mはクラス生成時の引数(例えばx = Class(5)
の引数5)が入る。インスタンス変数self.m
が電卓内のメモリを表し、そのメモリを引数で初期化する。引数がなければデフォルト値の0にする。インスタンス数を記録するクラス変数Calc.total_calculators
を1だけ増やすadd
とsub
はそれぞれ足し算と引き算のメソッド。2つの引数が与えられたときはその和または差を計算し、メモリに入れ、計算結果を返す。引数が1つの時は、メモリと引数の和または差を計算し、メモリに入れ、結果を返すreset
はメモリをゼロにする
電卓を使ってみる
以下は電卓クラスのインスタンスを操作する例です。
# 初期値を指定してインスタンスを生成 calc = Calc(10) # 二項の加算 print(calc.add(3, 4)) # 結果: 7 # 一項の加算(記憶している値に加える) print(calc.add(5)) # 結果: 12 # 二項の減算 print(calc.sub(10, 3)) # 結果: 7 # 一項の減算(記憶している値から引く) print(calc.sub(2)) # 結果: 5 # メモリのリセット calc.reset() print(calc.m) # 結果: 0 # 作成されたインスタンスの数 print(Calc.total_calculators) # 結果: 1
クラスを使うメリット
クラスを使うと、次のような場面でプログラムが整理しやすくなります。
- データと操作の統一管理: 計算結果(データ)と加算・減算(操作)を1つのクラスにまとめられる。
- 再利用性の向上: 必要に応じて複数のインスタンスを作り、それぞれ独立して動作させられる。