Developer

【Python独学】クラス変数・クラスメソッドの定義
2021.03.01
Lv1

【Python独学】クラス変数・クラスメソッドの定義

Pythonは、オブジェクト指向と呼ばれるプログラミング言語の一つです。
今回は、クラス変数・クラスメソッドの定義に関する基本的な部分を紹介します。


クラス変数・クラスメソッドについて

前回までの記事で、インスタンス変数とインスタンスメソッドについて触れてきました。
実は、クラス自身も変数とメソッドを持つことが出来ます。
それぞれをクラス変数、クラスメソッドと呼びます。
今回は少し難しい話になりますが・・・
バイクの製造台数を管理する、という例で後半は話を進めていきたいと思います。

・クラス変数について
クラス変数とは、インスタンスで共有して使えるような変数です。
インスタンスとは一つのクラスからいくつでも作ることが出来ますが、それぞれのインスタンスが使えるような変数がクラス変数です。

じゃあクラス変数があれば何が出来るの?と思うかもしれませんね。
例を見ながら、確認してみましょう。

次のようなクラスが定義されているとします。

#クラスの定義
class Motorbike:
    maker = "Python自動車"  #クラス変数の初期化
    
    #初期化メソッド
    def __init__(self,color = "black", displacement = "750CC") :
        self.color = color
        self.displacement = displacement
        self.mileage = 0

3行目の部分で、makerという変数に、”Python自動車”という文字列の値が設定されています。。
manufacturerがクラス変数になるのですが、少しイメージしてみください。。
このクラスは、バイクを作り出すための設計図になります。。
インスタンス化されることで1台ずつバイクが作られていくような感じですが、どのバイク(インスタンス)も、メーカーはPython自動車であるというようなイメージになります。

上記は大雑把な例えではありますが・・・
インスタンスが共通して持てるような変数、それがクラス変数になります。

もう少しクラス変数の存在意義を説明したいのですが、その前に一旦、クラスメソッドについてお話したいと思います。

クラスメソッドについて

クラスメソッドとは、インスタンスで共有して使えるようなメソッドです。

クラスメソッドで何が出来るのか、ということを見ていきましょう。
※上述したクラス変数も登場します。

以下が書式となります。

class クラス名:
    @classmethod
    def クラスメソッド名(cls,引数2,引数3,引数4,・・・):    #clsはクラス自身を意味する
        ステートメント                                      #ステートメントに処理を記述
           ・・・

初期化メソッドの書式と似ていますね。
クラスメソッドの場合には、2行目のように@classmethodと書いた上で、3行目のようにdef文での定義が始まります。
def文でクラスメソッドの引数としてclsとありますが、これはクラス自身を表します。省略することは出来ません。

では、実際に定義してみましょう。
以下では、

#クラスの定義
class Motorbike:
    manufacturer = "PythonEngine"  #クラス変数
    count = 0                      #クラス変数countを初期化
    
    #クラスメソッドの定義
    @classmethod
    def product_count_up(cls) :
        cls.count += 1                   #クラス変数countの値をプラス1する
        print(f"累計製造数:{cls.count}")
        
    #初期化メソッド
    def __init__(self,color = "black", displacement = "750CC") :
        self.color = color
        self.displacement = displacement
        self.mileage = 0
        Motorbike.product_count_up()   #初期化メソッドの実行の際に、クラスメソッドを実行

bike1 = Motorbike() #引数無しでインスタンス化
C:\Python> python 13-4-3.py
累計製造数:1

“累計製造数:1″という出力結果がありますね。
上記プログラムの中で、どこの部分によってこの文字列が出力されるに至ったと思いますか?
順にみていきましょう。
①インスタンス化(19行目)の際に、初期化メソッドが実行
②初期化メソッドの処理の中で、クラスメソッドが実行(17行目)
③クラスメソッドの処理の中で、クラス変数count(初期値0)がプラス1され、print()関数で出力(9,10行目)
という流れになります。

では、インスタンスの数を増やしてみましょう。
上記のプログラムに、インスタンスを一つ増やしてみます。

#クラスの定義
class Motorbike:
    manufacturer = "PythonEngine"
    count = 0
    
    #クラスメソッドの定義
    @classmethod
    def product_count_up(cls) :
        cls.count += 1
        print(f"累計製造数:{cls.count}")
        
    #初期化メソッド
    def __init__(self,color = "black", displacement = "750CC") :
        self.color = color
        self.displacement = displacement
        self.mileage = 0
        Motorbike.product_count_up()

bike1 = Motorbike()
bike2 = Motorbike("red") #引数有りでインスタンス化
C:\Python> python 8-4-5.py
累計製造数:1
累計製造数:2

累計製造数が2、となりました。
これは、クラス変数countの値が保持されているからです。

では、プログラムを工夫して、もっと意義のある使い方をしてみましょう。
製造されたそれぞれのバイクに、製造番号を振るようなイメージをしてみてください。

#クラスの定義
class Motorbike:
    manufacturer = "PythonEngine" 
    count = 0
    
    #クラスメソッドの定義
    @classmethod
    def product_count_up(cls) :
        cls.count += 1
        print(f"累計製造数:{cls.count}")

    #初期化メソッド
    def __init__(self,color = "black", displacement = "750CC") :
        Motorbike.product_count_up()
        self.color = color
        self.displacement = displacement
        self.mileage = 0
        self.serial_number = Motorbike.count #インスタンス化された時点でのクラス変数countの値を、インスタンス変数serial_numberに格納

bike1 = Motorbike()
bike2 = Motorbike("red")
print(bike1.serial_number)  #bike1のserial_numberを表示
print(bike2.serial_number)  #bike2のserial_numberを表示
C:\Python> python 8-4-5.py
累計製造数:1
累計製造数:2
1
2

初期化メソッドの中で、インスタンス化された時点でのクラス変数countの値をインスタンス変数serial_numberに格納しています(18行目)。
即ち、インスタンスbike1とbike2は、serial_number(製造番号)として1,2という数字が順番に振られるという形になります。

このように、あるクラスから作られた各インスタンスが共有して使えるような値とメソッドが、クラス変数とクラスメソッドになります。
インスタンス変数とインスタンスメソッドはそのインスタンス自身でしか使えませんが、クラス変数とクラスメソッドはそのクラスの各インスタンスで使えるようなものになります。
違いについては押さえておきましょう。


まとめ

クラス変数とは、インスタンスで共有して使えるような変数です。
クラスメソッドとは、インスタンスで共有して使えるようなメソッドです。


前回の確認問題の回答例

前回の記事はこちら→【Python連載】インスタンスメソッドの定義

次のようなプログラムがあります。

class Motorbike:
    def __init__(self,color = "black", displacement = "750CC") : #初期化メソッドの定義
        self.color = color
        self.displacement = displacement
        self.mileage = 0

    def bikeRunning(self, mile) :
        self.mileage += mile

    def show(self) : #引数はselfのみ
        print(f"色:{self.color}")
        print(f"排気量:{self.displacement}")
        print(f"走行距離:{self.mileage}")

bike1 = Motorbike()
bike1.bikeRunning(100)

bike1.mileage = 0
bike1.bikeRunning(300)
bike1.show()

コンソールにどのような出力がされるかを考えてみましょう。
(18行目に要注意)

・以下が解答になります。
次のような出力がされます。
色:black
排気量:750CC
走行距離:300

16行目でbikeRunningメソッドが引数100を渡されて実行されますが、18行目でmileageを0としています。
ですので、19行目でのbikeRunningメソッドの分だけ、走行距離は加算される形になるため、上記のような出力となります。

連載目次

独学で学ぶ Pythonプログラミング 連載目次