はじめに
pythonにおけるクラスについてまとめていこうと思います.
プログラミング初学者な新人への教育資料をイメージして書きました.
クラスとは
クラスとは、一言で言えば 設計図 のようなものだ。
ここで???がついてしまうと思うので、具体的に例を挙げて理解の助けになれば幸いです。
身長、体重からBMIの値を表示するプログラムを作るとしよう。
名前 | 身長 [cm] | 体重 [kg] | BMI |
---|---|---|---|
Aさん | 170 | 63 | -- |
Bさん | 180 | 100 | -- |
Cさん | 165 | 45 | -- |
ちなみにBMIの値は、$$BMI=\frac{体重}{身長^{2}}$$で求められます. (最近増えてきたので私も気を付けなければいけません)
では、BMIを求めるために「関数を作ればいいじゃないか!」という発想になると思います.
しかし, 誰が何センチで誰が何キロなどを別個に管理していると意外と大変な思いをすると思います.
今回の例では3人ですが, 20人とかになると何が何やらという状態になるのではないだろうか.
そんなときにクラスを作り, データや関数などを管理をすると結構楽になります.
クラスのイメージ
クラスに対してどのようなイメージを持てばよいか例を挙げたいと思います.
今回の例ではBody Conditionというクラスを作ってみましょう.
このクラスには身長と体重の変数(メンバ)、そして自分の持っているBMIを算出する関数(メソッド)を持たせます.
図のようにBody ConditionというテンプレートにAさんの持つ身長と,体重という情報を流し込んでAさんのオブジェクトを実体化させます. これをインスタンス化 といいます.
つまり クラスというのはあくまでも金型で実体はないということになります.
Bさん、Cさんについても同じような手続きを踏めばBさん, Cさんのオブジェクトを作成できます.
実際にコードベースでどのようにクラスを定義していくのか次のチャプターで説明します.
クラスの定義
クラスの定義は、次のように行われる。
class クラスの名前:
def 関数名( self,... ):
#処理
def 関数名( self,... ):
#処理
ここで、クラスが持つ関数のことをメソッドと呼びます. 約束事としてメソッドの一つ目の引数は原則 self にしなければいけません.
この self と呼ばれる謎の変数ですが, これはインスタンス, つまりメソッドが呼び出された時にクラスから作られた実体が渡されます。
self = 自分自身(インスタンス) と思ってくれてよいと思います.
後ほど説明に出てきますが, selfの役割として インスタンス変数 や メソッド のアクセスに用いたりします.
クラスの変数について
クラスの変数には大きく二つあり、インスタンス変数とクラス変数というものがある。
インスタンス変数
先ほどの例にあったように、体重や身長といった様な変数があったと思います. これを インスタンス変数 と呼びます.
このインスタンス変数はメソッドの中で定義します.
ここで、「BMIを求めるメソッドで使いたいのにメソッドで定義するとはこれ如何に?」と思うかもしれません.
その説明の前に、特殊メソッドとして、イニシャライザ というメソッドが用意されており、これの説明をします. (コンストラクタという呼ばれ方もあったりする。)
class クラスの名前:
def __init__( self,... ):
self.変数名=引数
def 関数名( self,... ):
#処理
インストラクタはインスタンス化時に一度だけ呼び出されるメソッドです. 文字通りですがインスタンスを初期化する, というイメージを持ってくれればよいです.
上のコードのように「__ init __ 」と明示することで、python側でイニシャライザだと解釈してくれます.
ちなみにpythonのお約束で__**__で囲ったものは特殊メソッドとなり, 他にもあるので調べてみてください.
そして, インスタンス変数は self.変数名
でアクセスできたりします.
もちろん普通のメソッド内でもインスタンス変数に代入することはできるが, クラスとして機能させるのに最低限必要な情報だったりはイニシャライザで行うべきと考えてます.
全体の流れとして、
- インスタンスを作成
- 作成時に、イニシャライザでインスタンス変数を持たせる。
- インスタンス変数を用いて、bmi算出メソッドを呼び出す。
といった流れになるだろう。
クラス変数
インスタンス毎に異なる値を持つ場合はインスタンス変数で定義しなければならないが、クラス内で共通した値を用いたい場合は、クラス変数というものを定義すると便利です. (ルートパスとかそんなの)
class クラスの名前:
変数名 = 値
def __init__( self,... ):
self.変数名=引数
def 関数名( self,... ):
#処理
クラス変数は、メソッドの外で定義して、selfを付けない。(クラス内をスコープとしたグローバル変数といったところでしょうか)
ちなみにインスタンス変数とクラス変数をまとめて属性と呼びます.
インスタンスの生成・属性/メソッドの呼び出し
実際にインスタンスを生成するときは、以下のコードのように宣言します.
obj = class(arg1, arg2, ... )
この引数はイニシャライザに渡す引数です.
生成時にイニシャライザが呼び出されるために渡さないと必ず怒られます.
インスタンスのメソッド、インスタンス変数、クラス変数にアクセスするためには次のように記述すれば、呼び出せます.
obj.method(引数1, ...)
obj.instance_var
obj.class_var
クラスを使ってみる
実際にクラスを作ってどのような挙動になるのか確認してみましょう.
まず、適当に以下のようなクラスを作ってみました.
class BodyCondition:
def bmi_calc( self ):
print("あいうえお")
→
最初の方でも言った通り、クラスというのはあくまで設計図であり、実体はないのだ.
つまり、これだけで実行しても特に何も起こりません. 宣言しただけなので.
それでは、このクラスからインスタンスを作ってbmi_calcメソッドを呼び出してみよう。
class BodyCondition:
def bmi_calc( self ):
print("あいうえお")
test=BodyCondition()
test.bmi_calc()
→ あいうえお
bmi_calcの処理内容が実行されていることが確認できました.
次にイニシャライザを作って, このイニシャライザがいつ実行されるのか見てみましょう.
class BodyCondition:
def __init__( self ):
print("初期化")
def bmi_calc( self ):
print("あいうえお")
test=BodyCondition()
→ 初期化
__init__メソッドを呼び出されていないにもかかわらず、出力にはイニシャライザの処理が実行されてますね.
先ほども書きましたが, まさに インスタンス"作成時に" 実行されていることが確認できました.
それでは、動作が確認できたところでbmi出力のクラスに書き直してみましょう.
class BodyCondition:
def __init__( self, arg_height, arg_weight ):
self.height=arg_height
self.weight=arg_weight
def calc_bmi( self ):
m_height=self.height/100.
bmi=self.weight / m_height / m_height
print( bmi )
a_body=[ 170, 63 ]
b_body=[ 180, 100 ]
c_body=[ 165, 45 ]
A=BodyCondition( a_body[0], a_body[1] )
B=BodyCondition( b_body[0], b_body[1] )
C=BodyCondition( c_body[0], c_body[1] )
A.calc_bmi()
B.calc_bmi()
C.calc_bmi()
A.weight
B.weight
C.weight
→
21.79930795847751
30.864197530864196
16.528925619834713
63
100
45
メソッドも実行できましたし, メンバも呼び出すことができましたね.
Bさんは少し生活習慣を見直したほうが良いかもしれません. Cさんはもっとお米食べろ.
このようにAさんに関する変数や機能である関数をまとめて一つのオブジェクトとして扱うことですっきりしますし, 何か機能を増やしたいといった時などはクラスにメソッドを追加すればよいだけなので拡張性にも対応できるようになります.
一連のプログラムをオブジェクトの集まりだとする考え方を オブジェクト指向 と言ったりもします
まとめ
今回の記事でクラスの基本的な考え方や使い方はまとめられたんじゃないかなと思います.
特にクラスはオブジェクト指向においてとても重要な考え方になりますのでpythonに限らず押さえておきたいところです.
ほかにも継承や基底クラス、ジェネリティクラス、データクラスなどさらに上級な概念があります.
今後、これらについてまとめる機会があったら記事にしようと思います.