Pythonのクラスの使い方とオブジェクト指向の考え方を理解する

  • このエントリーをはてなブックマークに追加

オブジェクト指向のプログラミングを覚えることで大規模なコードを効率よく記述することが可能です。ここではPython初心者である筆者がPythonのクラス(class)の使い方とクラスを使うメリットを学習した結果をまとめます。

こんにちは。wat(@watlablog)です。Python関連のブログを開設して約1年が経ちますが、ここではいよいよPythonクラス(class)を学んでみます

クラス(class)とは?

クラスの概念

プログラミングにおけるクラス(class)とは、オブジェクトを生成するために必要な設計図に相当するものです。

クラスは設計図であるため、そのオブジェクトがどういったデータを必要とし、どのような振る舞いをするかといったことを記述する必要があります。

また設計図は所詮方法を述べたものにしか過ぎないため、実際に使うためには実体を作ってから動作させる必要があります。

設計図を使って実体を作る様から、しばしばクラスはたい焼きを作るような「金型」、実体のことを実際に材料を流し込んで作った「商品」と表現されることがあります。

クラスはデータや関数をまとめることが出来るため、いたずらに変数を増やしたり、同じコードをあちこちに書いたりしなくても良くなるメリットがあります。

プログラミングに限った話ではありませんが、コードはKISSの原則(Keep It Short and Simple:短く単純にしておくように)に則ることが正とされています。
クラスを使いこなすことでKISSの原則に従うプログラミングが可能になるはずです。

クラスと関数の違い

これまで当WATLABブログではPythonプログラミング関連で150記事以上紹介してきましたが、コードの効率化という面ではdef関数しか使っていません。
※def関数については「Pythonの関数 def文の使い方!引数や別ファイル式も解説」を参照下さい。

関数は計算を1つにまとめることが出来ますが、クラスは複数の関数もまとめることが可能であり、関数の上位の存在となります。

クラスの中で使われる関数のことをメソッドと呼びます。メソッドについては後程説明します。

Pythonにおけるクラスの作り方(コードで解説)

それでは早速Pythonプログラムでクラスを定義しながら、クラスがどんなものなのかを見ていきたいと思います。

僕自身プログラミングは初心者(専門的に教育を受けたことは無いし、仕事で使ったとしても我流レベル)なので、今回は色々なWebページや書籍を参照しました。

しかし、「Pythonのクラスはこうやって定義します」「クラスは〇〇を定義して…」といった手段ベースの説明が多く、クラスを使うと一体どんなメリットがあるのか、サンプル以外のクラスはどうやって設計するのかといった内容が一発で理解できるような説明は中々見当たりませんでした(探し方が悪いとか、頭が固いとかもあるかもですが…)。

ここでは最終的に僕が理解した内容を、実際にクラスを定義する流れに沿って説明していこうと思います。そのため、中には間違った表現を使っている所があるかも知れません。もしお気付きの点がございましたらコメントを頂けたら助かります!

はじめにクラスの設計が必要

早速と言いましたが、Pythonクラスの記述方法を語る前に、まずはクラスを設計する作業が必要です。

多分ここが一番重要なポイントだと思います。適切に設計しないと、せっかくコードを効率的に書こうと思っても他人が理解し難かったり、後で仕様追加がし難いクラスが出来上がってしまうかも知れません。

仕様決め

…とは言えここでは簡単に、「三角形の計算をまとめたクラス」で説明をしていきます。下図に今回作るクラスの概要を示します。

Pythonのクラスを理解するためのサンプルクラス設計

ここではクラスに
・line1
・line2

という2つのデータを渡して、
・三角形の斜辺の長さlen
・三角形の面積area

という2つの計算値を求める関数をまとめます。

内容自体はdef関数でも簡単に書くことが出来ますが、クラスで作っておけば後で別の計算を追加したい時に、クラス内の関数を追加することで共通の変数を使うことが可能になります。

def関数の場合は追加した関数に計算をさせる時に、再度変数を渡さないとなりません。クラスで作っておけば変数を共有してどんどん追加修正が出来るというメリットがあるみたいですね。

今回は上記クラスを実際にPythonで作ってみます。

クラスの命名規則

始めに、クラスの名前を決めます。
クラスは、それがクラス名であることがわかるように一般的に暗黙の了解となっている命名規則があるそうです。

以下の図にPythonのクラスで推奨されているクラス名の付け方を示します。

クラスの命名規則

def関数の場合はfunction_nameのように、小文字の英単語とアンダーバーを組み合わせて名前を付けるのが良いらしいですが(知らなかった…)、クラスの場合は英単語の先頭を大文字にすることで単語の区切りを明確にし、def関数と区別をするそうです。

それでは命名規則に従って、三角形の計算をするクラスを作ってみます。
今回は以下のコードのように「TriangleCalc」と命名してみました。

コンストラクタとプロパティの記述をする

クラスに命名をしたので、次は中身を作っていきます。
次に作るのはコンストラクタ(Constructor)です。

コンストラクタとは、設計図としてのクラスを使って実体(インスタンス)を生成する際に最初に実行される関数の事です。主に初期化に使われることから初期化メソッドとも呼ばれます。

今回作成するクラスのコンストラクタの宣言部分を以下のコードに示します。defの前にインデントが必要なことに注意して下さい。

Pythonのコンストラクタはdefを使って書きます。initをアンダーバー2個ずつで挟み、def関数で書くコンストラクタの名称は「__init__」という名称を使うことが決まりです。

クラスは後で実体(インスタンス)を作って使う時に、それぞれ固有の名称を付けることが出来ます。どんな名称が設定されても自分自身のデータを参照することが出来るよう、このdef関数(メソッド)の第1引数には「self」を指定するのが決まりです。

その他の引数には、それぞれ上で設計したline1, line2を設定します。
もしクラスで扱うメソッドを追加し、新たな引数が必要になった場合はこの括弧内に追加していきます。

続いて、コンストラクタに初期化の内容を記載します。
初期化が必要なのは変数です。ここで設定した変数群はクラスのプロパティ(Property)と呼ばれます。

以下のコード例のように、引数として参照している値を変数に格納したり、これからメソッドで計算するアウトプットを入れる変数の初期値を決めたりといった作業を行います。

ここで、先ほどと同様にクラスは生成されたインスタンス毎にこの変数を使い分けたいので、変数名には「self.」を付けて定義する決まりがあります。

そういえば、外部ライブラリを使う時もよくこのドット「.」を使っていましたね。クラスの中身を理解することで、プログラム内で今どんな作業をしているかがクリアになってきそうです。

メソッドを追加する

コンストラクタを書いた後は、メソッド(クラス内で使用する関数)を追加していきます。ここはdef関数の書き方と同じなので、もしdef関数の書き方に不安がある方は「Pythonの関数 def文の使い方!引数や別ファイル式も解説」をご覧下さい。

但し、コンストラクタ部の変数を使う時に、「self.」を頭に付ける所が注意点です(メソッドで新たに引数を設定する場合はselfは要りません)。

以上で基本的なクラスが出来上がりました。
以下より作ったクラスを実際に使ってみましょう。

Pythonにおけるクラスの使い方(コードで解説)

インスタンスを生成する

作ったクラスを実際に使うためにインスタンスを生成します。
インスタンスの生成は、「インスタンス名=クラス名(引数)」で行います。

以下のコードに例を示します。今回は大きさの違う三角形を2つインスタンス化してみました。

メソッドを実行する

最後にメソッドを実行して結果の値を取り出してみます。
メソッドの実行は、先ほど生成したインスタンス名とメソッド名をドットで繋ぎ「インスタンス名.メソッド名(引数)」と書きます。

今回はコンストラクタ部で引数を渡していますが、追加で引数が必要である場合はこの括弧内に配置させます。

計算された変数を取り出すためには、「インスタンス名.変数名」と書きます。

サンプルクラス構築と結果確認までの全コード

ここまでのクラス構築から実行までの全コードを繋げたもの(コピペ用)を以下に示します。

このコードを実行すると以下の結果を得ます。
同一のクラス、メソッドを使い、同一の変数名で値を取り出しているにも関わらず、インスタンス固有の結果を得るということが出来ました。

このように、同じような振る舞いをするもの(オブジェクト)を一つ一つ作らないで共通のメソッドや変数をまとめ、インスタンス毎に使いまわしが出来るようプログラミングすることをオブジェクト指向プログラミングと呼ぶそうです(色々説明の仕方があるため曖昧な表現ですが)。

オブジェクト指向の考え方はよくRPGのキャラクターや車に例えられています。

車をプログラム上で表現しようとした時、1台1台毎に振る舞いを書いていたら、もし修正が必要な時に同じ修正を全ての台数でしなければいけません。

オブジェクト指向プログラミングで書いておけば、機能追加で修正する個所はクラスの内部だけで済みます。

大規模プログラムになればなるほどオブジェクト指向プログラミングの考え方を使わないと大変なことになりそう!是非マスターしたい所ですね!

Pythonクラスのさらに便利な機能紹介(コードで解説)

上記まででPythonのクラス構築と使用の基本的な説明は終了です。使うだけであれば上記内容で十分ですが、クラス・オブジェクト指向の考え方にはまだまだ便利な機能があります。ここではいくつかの便利機能をPythonコードを書きながら紹介していきます。

デストラクタで安全なリソース管理をする

デストラクタ(Destructor)とは、コンストラクタの対義語であり、具体的には生成したインスタンスを削除するメソッドです。

デストラクタを使えば、プログラムが実行されている間にインスタンスを削除することが出来ます。

インスタンスを生成するということは、メモリを割り当てるアロケーション(Allocation)がされます。大規模プログラムであれば大量のインスタンスを生成することもあるでしょう。

生成したインスタンスが使用されなくなったら、適宜デストラクタで明示的に削除した方がリソースの有効な使い方が可能になります

Pythonにおけるデストラクタは「__del__」で定義します。

デストラクタの実行は「del インスタンス名」と書きます。

但し、Pythonはガベージコレクション(Garbage Collection)という不必要なメモリを自動で解放してくれる機能を持っています。

オブジェクトの種類(Python以外の言語を使用したライブラリ起因のオブジェクト等)によっては解放してくれないものもあるそうなので、デストラクタの使用はメモリに関する一定の知識が必要であると考えられます。

継承で既に作ったクラスを再利用する

クラスを使う上でおそらく最も効率向上に有効な概念に「継承(Inheritance)」があります。

継承とは、既に作成したクラスを受け継いで新しいクラスを作ることです。

継承することで親クラスが持つメソッドや変数を子クラスから利用することが可能です。

以下に継承を含んだサンプルコードを示します。
ここでは先ほどのTriangleCalcクラスを継承したTriPrismクラスを追加しています。継承は「class 子クラス名(親クラス名):」で行います。

TriPrismクラスには高さheightを新たな引数として設定し、親クラスで計算した面積areaを使って体積volumeを計算するメソッドを記述しています。

以下が実行結果です。
このように継承を行うことで、子クラスで親クラスのメソッドを使えるようになります。

この程度であれば「親クラスを編集してvolumeを計算させるメソッドを追加すれば良いじゃないか」と思われますが、大規模なプログラムになるとクラスは様々な場所で使われます。子クラスを作っておけば親クラスを編集しないので、他のアルゴリズムに対するバグの元を作ることにならないメリットがあります

super()を使った継承の例

Pythonで継承を行う時は、super()を使うことが推奨されています。以下のコードは上記継承コードと全く同じ動作をしますが、子クラスTriPrismでsuper()を使っている所が異なります。

子クラスのコンストラクタ部で「super().__init__(継承したい変数)」と定義しています。

実行結果は先ほどと同じになるので割愛しますが、super()は多重継承という複数のクラスを継承する時に特に推奨されているそうです。この内容は結構上級レベルになりそうなので、別記事でそのうちまとめようと思います。

オーバーライドで内容を書き換えて継承する

コーディング効率を向上させるクラスの機能の1つにオーバーライド(Override)があります。

オーバーライドとは、親クラスを継承はするけど、メソッドやプロパティの一部を変更するといういわゆる上書き機能のことです。

以下のコードはこれまで紹介してきたコードの子クラス部分のみを紹介しています。親クラスであるTriangleeCalcを継承していますが、斜辺を求めるcalc_lenをオーバーライドしています。

Pythonのオーバーライドの方法は「メソッドやプロパティに同じ名前を使う」ことで実現されます。

オーバーライドをすることで親クラスからの小変更をすることが出来るので、「かゆいとこに手が届く」ようにコードを書いていくことが可能になりますね。

まとめ

本ページではPythonのクラスの使い方を説明して来ました。
基本的なクラスは、
①クラスでまとめる変数と関数の仕様を設計する
コンストラクタでプロパティの初期化を書く
メソッドを書く

で構築することができます。

そしてクラスを使うためには、
インスタンスを生成する
メソッドを実行する

という手順が必要ということがわかりました。

上記基本的な使い方の他にも、
デストラクタでリソースの節約が出来る
・クラスの継承で効率の良いコード再利用が出来る
オーバーライドで親クラスの一部を変更した子クラスが作れる

といった様々なテクニックがあることもわかりました。

ここで紹介した使い方はクラスのほんの一部の内容にしか過ぎません。
クラスの恩恵を得るためには継承やオーバーライド、複数クラスの連携を自由自在に使いこなす必要があります

今回僕はPythonにおけるクラスの基本を調べましたが、ようやく使い方がわかったレベルです。

そろそろ書籍やWebでも他の人の書いたコード(ライブラリの中身を見るのも良いかも)が理解できるようになってきたので、今後は上手い人のコードを真似るということにも挑戦してみようと思います。

オブジェクト指向は本当に奥が深いです!まだまだ練習あるのみ!
X(Twitter)でも関連情報をつぶやいているので、wat(@watlablog)のフォローお待ちしています!

SNSでもご購読できます。

コメント

コメントを残す

*