LoginSignup
0
0

pythonにおける仮想関数と純粋仮想関数

Posted at

仮想関数

仮想関数とは?

仮想関数とは、基底クラスの関数を、サブクラス(派生クラス)で、上書き(オーバーライド)できる関数のこと。

使いどき

デフォルト処理を設定したい場合などに使用する。
継承先で、当該関数を上書きして使用することで、好きな処理内容に書き換えることもできる。

純粋仮想関数

純粋仮想関数とは?

純粋仮想関数とは、仮想関数に、実装をもたない(処理を書かない)関数のこと。
仮想関数とは異なり、継承先で、基底クラスで定義した、関数名(引数・戻り値を含む)を”必ず実装”しなければならないことが大きな特徴です。

使いどき

最初から実装すれば良いじゃん。と私も思ってました。が、同じような行為や、ある程度同じ処理手順のとき、いちいち実装なんてしていたら、仕事だったら、時間がいくらあっても足りません。

どんなときか?
私は、料理を例にとって、考えていきました。

料理をするには、一連の調理工程(作業手順)があり、最初(材料買い出し)と、最後(片づけ)は同じことをする。とします。

しかし、料理によっては、調理工程は様々ですよね。
オブジェクトとしては、料理という塊は意識したい。
こういったときに利用するといいと思います。

具体的な例(設計)

料理を調理(Cook)というクラスとして考えてみましょう。

調理(Cook)クラスの、料理する(DoCook)を呼び出すと、以下の順に処理を行うとします。

  1. 材料の買い出し(普通のメンバ関数)
  2. 下ごしらえ(仮想関数)
  3. 調理工程A(純粋仮想関数)
  4. 調理工程B(純粋仮想関数)
  5. 調理工程C(純粋仮想関数)
  6. 皿に盛り付ける(普通のメンバ関数)
  7. 片づける(普通のメンバ関数)

つぎに、「調理」Cookクラスを継承した、「ラーメンの調理」RamenCookクラスを作成したとします。
以下が、ラーメンの調理(RamenCook)の処理手順です。

  1. 材料の買い出し(普通のメンバ関数)
  2. 下ごしらえ(仮想関数)
    →オーバーライド
    • 野菜を切って水に浸す。
  3. 調理工程A(純粋仮想関数)
    →継承先で実装
    • 野菜をゆでる。
  4. 調理工程B(純粋仮想関数)
    →継承先で実装
    • お湯を沸かして、麺をゆでる。
  5. 調理工程C(純粋仮想関数)
    →継承先で実装
    • 卵を割って、スープの素を入れる。
  6. 皿に盛り付ける(普通のメンバ関数)
  7. 片づける(普通のメンバ関数)

実装

では、こんどは、実装してみましょう。
設計した結果を、反映すると以下のような実装になります。

################
## Cook.py ##
################
from abc import ABC, abstractmethod

# 調理クラス
class Cook(ABC): 

    # 買い出し(普通のメンバ関数)
    def __shopping(self):
        print(" 買い出しをする")
    # 下ごしらえ(仮想関数)
    @abstractmethod
    def _food_preparation(self):
        print(" 野菜を切る")
    # 調理工程A(純粋仮想関数)
    @abstractmethod
    def _cooking_process_A(self):
        pass
    # 調理工程B(純粋仮想関数)
    @abstractmethod
    def _cooking_process_B(self):
        pass
    # 調理工程C(純粋仮想関数)
    @abstractmethod
    def _cooking_process_C(self):
        pass        
    # 皿に盛り付ける(普通のメンバ関数)
    def __cleaning_up(self):
        print(" 皿にもりつける。")
    # 調理
    def do_cooking(self):
        self.__shopping()
        self._food_preparation()
        self._cooking_process_A()
        self._cooking_process_B()
        self._cooking_process_C()
        self.__cleaning_up()
        return True

################
## Ramen.py ##
################

class RamenCooking(Cook):

    # 下ごしらえ(仮想関数)
    def _food_preparation(self):
        print(" 野菜を切って水につける")
    # 調理工程A(純粋仮想関数)
    def _cooking_process_A(self):
        print(" お湯を沸かす")
    # 調理工程B(純粋仮想関数)
    def _cooking_process_B(self):
        print(" 野菜を炒める")
    # 調理工程C(純粋仮想関数)
    def _cooking_process_C(self):
        print(" 麺をゆでる")

################
## FriedRice.py ##
################

class FriedRice(Cook):

    # 下ごしらえ(仮想関数)
    def _food_preparation(self):
        print(" ネギ・チャーシューを切る")
    # 調理工程A(純粋仮想関数)
    def _cooking_process_A(self):
        print(" フライパンを温める")
    # 調理工程B(純粋仮想関数)
    def _cooking_process_B(self):
        print(" 溶き卵をフライパンへ入れて、ご飯と混ぜる")
    # 調理工程C(純粋仮想関数)
    def _cooking_process_C(self):
        print(" ネギ・チャーシューを加えて味付けする")


# メインの実行部分
if __name__ == "__main__":
    print("①ラーメンを作る")
    ramen_cooking = RamenCooking()
    ramen_cooking.do_cooking()
    print("")
    print("②チャーハンを作る")
    fired_rice = FriedRice()
    fired_rice.do_cooking()

実行結果

①ラーメンを作る
 買い出しをする
 野菜を切って水につける
 お湯を沸かす
 野菜を炒める
 麺をゆでる
 皿にもりつける。

②チャーハンを作る
 買い出しをする
 ネギ・チャーシューを切る
 フライパンを温める
 溶き卵をフライパンへ入れて、ご飯と混ぜる
 ネギ・チャーシューを加えて味付けする
 皿にもりつける。

※実際には、importを指定する必要があります。

そして、今回は、ラーメン以外にも、チャーハンクラスを作成し、工程が異なるが
調理という枠の中で、表現をしてみました。

以上。

0
0
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0