yoshiislandblog.net
元営業の駆け出しアラサーSEが、休日にMACと戯れた際の殴り書きメモ。日々勉強。日々進歩。

Pythonでポリモーフィズムを理解する〜オブジェクト指向でなぜつくるのかを読んで〜

2021-01-21

オブジェクト指向でなぜつくるのか 第2版を読んでの話の続き

オブジェクト指向でなぜつくるのか 第2版

オブジェクト指向でなぜつくるのか 第2版

参考:Pythonで、グローバル変数、インスタンス変数、ローカル変数のスコープをざっくり理解する〜オブジェクト指向でなぜつくるのかを読んで〜

  1. イントロ
  2. なぜポリモーフィズムが必要なのか
  3. ポリモーフィズムを使った解決法

イントロ

オブジェクト指向の三大要素として、「カプセル化」「継承」「ポリモーフィズム」の三つがある

カプセル化 → わかる
継承 → わかる
ポリモーフィズム → …???

ポリモーフィズムだけ読んでも理解できず、ネットで検索

色々な記事を読んでも、「ポリモーフィズムは多様性だ!」というキーワード以外は、記事によって言っていることがバラバラに思え、
理解にすこぶる時間がかかったので、この記事では違った視点で理解した内容をメモ


なぜポリモーフィズムが必要なのか

次は本題のポリモーフィズムの話

ポリモーフィズムは、サブルーチンとは逆で、異なる処理の呼び出し方をまとめる、という考え方

例えば、このようなイケてないコードがある

% cat polymorphism_test.py
#!/usr/bin/env python
# coding: utf-8

# クラス1
class American():
  def greeting(self,greeting):
    if greeting == "Hello":
      return "Hello"
    else:
      return "Huh?"

# クラス2
class Spanish():
  def greeting(self,greeting):
    if greeting == "Hola":
      return "Hola"
    else:
      return "Eh?"

# クラス3
class French():
  def greeting(self,greeting):
    if greeting == "Bonjour":
      return "Bonjour"
    else:
      return "Hein?"


# 実行
if __name__ == "__main__":
  american = American()
  print(american.greeting("Hello"))

  spanish = Spanish()
  print(spanish.greeting("Hola"))

  french = French()
  print(french.greeting("Bonjour"))

実行結果は以下の通り

% python polymorphism_test.py
Hello
Hola
Bonjour

これの何が問題かというと、呼び出し方がスマートではない

具体的にはこの部分

# 実行
if __name__ == "__main__":
  american = American()
  print(american.greeting("Hello"))

  spanish = Spanish()
  print(spanish.greeting("Hola"))

  french = French()
  print(french.greeting("Bonjour"))

このバラバラの呼び出し方を統一するのがポリモーフィズム

どのクラスの中にも同じ「greeting」というメソッドがあるが、
以下のように、どれも「greeting_people()」関数で呼び出せることを目指す

# 実行
if __name__ == "__main__":
  american = American("Hello")
  print(greeting_people(american))

  spanish = Spanish("Hola")
  print(greeting_people(spanish))

  french = French("Bonjour")
  print(greeting_people(french))

ポリモーフィズムを使った解決法

では、呼び出し方を統一するにはどうすれば良いか

まずは、バラバラ存在するクラスたちを「親クラス」の元にまとめる
先ほどのコードでは、親クラスが存在しなかったので、「People()」というクラスを作成する

class People():

子クラスは、引数に親クラスの名前を書いておく

class American(People):

全体イメージは以下

# 親クラス
class People():
  # 処理は省略

# 子クラス1
class American(People):
  # 処理は省略

# 子クラス2
class Spanish(People):
  # 処理は省略

# 子クラス3
class French(People):
  # 処理は省略

次に、呼び出し方を統一するための関数を作る
それぞれのクラスの「greeting()」メソッドを呼び出していたのを、「greeting_people()」関数で呼び出せるようにする
具体的な書き方は以下の通り。「obj」の部分は何でも良い

# 呼び出し方を統一
def greeting_people(obj):
  return obj.greeting()

コード全体は以下の通り

% cat polymorphism_test.py
#!/usr/bin/env python
# coding: utf-8

# 親クラス
class People():
  def __init__(self, greeting):
    self._greeting = greeting
  def greeting(self):
    if self._greeting == "Hello":
      return "Hi"
    else:
      return "What?"

# 子クラス1
class American(People):
  def greeting(self):
    if self._greeting == "Hello":
      return "Hello"
    else:
      return "Huh?"

# 子クラス2
class Spanish(People):
  def greeting(self):
    if self._greeting == "Hola":
      return "Hola"
    else:
      return "Eh?"

# 子クラス3
class French(People):
  def greeting(self):
    if self._greeting == "Bonjour":
      return "Bonjour"
    else:
      return "Hein?"


# 呼び出し方を統一
def greeting_people(obj):
  return obj.greeting()


# 実行
if __name__ == "__main__":
  american = American("Hello")
  print(greeting_people(american))

  spanish = Spanish("Hola")
  print(greeting_people(spanish))

  french = French("Bonjour")
  print(greeting_people(french))

実行結果

% python polymorphism_test.py
Hello
Hola
Bonjour

ポリモーフィズムによって、行数増えてるじゃん、とも思うが、
オブジェクト指向プログラミングでは、保守性が重要なので、こちらの方が良いらしい

以上。