おもこん

おもこんは「思いつくままにコンピュターの話し」の省略形です

Python初級者のお勉強ノート(11)クラス、インスタンス、メソッド

クラスの説明からは「初心者」から「初級者」にタイトルを変えることにします。 カッコ内の番号は通し番号にします。

プログラムをクラスという仕組みにまとめることができます。

例えば、電卓プログラムを作るとしましょう。
プログラムの中の次のような部分をまとめてクラス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つの引数abがあります。

外部からこのメソッドを呼ぶためには、まずインスタンスの生成が必要です。 仮に生成されたインスタンスxとします。 すると、このメソッドはx.add(10, 20)のような形で呼び出されます。 呼び出し時には、関数定義のabの部分だけが引数に書かれ、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だけ増やす
  • addsubはそれぞれ足し算と引き算のメソッド。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つのクラスにまとめられる。
  • 再利用性の向上: 必要に応じて複数のインスタンスを作り、それぞれ独立して動作させられる。