8.1 ポリモーフィズム
ポリモーフィズムは、オブジェクト指向プログラミング(OOP)の基本概念の1つで、異なるクラスのオブジェクトに同じインターフェースを使わせることができるんだ。
Pythonでは動的型付けと継承を通じてポリモーフィズムが実現されるんだ。ポリモーフィズムの重要な側面はメソッドのオーバーロードと、基本クラスのメソッドをサブクラスのメソッドで置き換えることだよ。
ポリモーフィズムの基本概念:
異なるオブジェクトに対する統一インターフェース:
ポリモーフィズムでは、異なるクラスのオブジェクトに対して統一されたインターフェースを使用でき、それぞれが異なるメソッド実装を持つことができるよ。
動的型付け:
Pythonでは変数の型は実行時に決まるので、関数は必要なインターフェースをサポートするすべてのクラスのオブジェクトを処理できるんだ。
継承:
クラス階層を作成でき、サブクラスは基本クラスのプロパティやメソッドを継承しながら、自分独自の動作をオーバーライドできるんだ。
ポリモーフィズムの例:
簡単なポリモーフィズムの例:3つのクラスはすべてmove()
という名前のメソッドを持っているんだ。これで、これらのオブジェクトに対して同時に動作するコードを書けるよ。
class Car:
def move(self):
pass
class Human:
def move(self):
pass
class Bird:
def move(self):
print("カァ!")
car = Car()
human = Human()
bird = Bird()
for it in [car, human, bird]:
it.move()
ここでの「共通インターフェース」はメソッド名、あるいはメソッドに引数があればそのシグネチャだね。
8.2 メソッドのオーバーライド
ポリモーフィズムはよく継承と一緒に使われ、クラス階層を作成する際に、基本クラスが共通インターフェースを定義し、サブクラスが特定の詳細を実装するんだ。
class Employee:
def get_salary(self):
return 1000
class FullTimeEmployee(Employee):
def get_salary(self):
return 5000
class PartTimeEmployee(Employee):
def get_salary(self):
return 3000
class Intern(Employee):
pass
def print_salary(employee):
print(employee.get_salary())
employees = [FullTimeEmployee(), PartTimeEmployee(), Intern()]
for employee in employees:
print_salary(employee)
この例は先ほどの例に似ているけど、重要な違いがあるんだ — 必ずしもget_salary
メソッドを持っている必要はないんだ。なぜなら、基本クラスに常にそのメソッドがあるからね。今やすべてのクラスの「共通インターフェース」は単なるget_salary()
メソッドではなく、Employee
クラスそのもの、そしてそのすべてのメソッドと属性なんだ。
8.3 サブクラスのメソッド呼び出し
以下のコードをよく見てみよう:
メソッドprint_salary()
は、基本クラスEmployeeのみに存在し、そこからまた基本クラスの別のメソッドget_salary()
を呼び出してる。さて、どんな給与が表示されるかな?
class Employee:
def print_salary(self):
salary = self.get_salary()
print(salary)
def get_salary(self):
return 1000
class FullTimeEmployee(Employee):
def get_salary(self):
return 5000
class PartTimeEmployee(Employee):
def get_salary(self):
return 3000
class Intern(Employee):
pass
employees = [FullTimeEmployee(), PartTimeEmployee(), Intern()]
for employee in employees:
employee.print_salary()
重要!
以下に書いてあることを読んでね — これは重要だよ。
もしFullTimeEmployee
クラスでprint_salary()
メソッドを呼び出した場合、メソッドは基本クラスのものが呼ばれる、なぜならクラス自身にそのメソッドは宣言されてないからね。
しかしこのprint_salary()
メソッドが呼び出すget_salary()
メソッドは、self
オブジェクトのもので、これはFullTimeEmployee
型であってEmployee
型ではないんだ。だから、FullTimeEmployee
クラスのget_salary()
メソッドが呼ばれるんだ!
さらに、FullTimeEmployee
クラスは必要に応じて基本クラスのget_salary()
メソッドを呼び出すことができる。例えば、基本給の割合で給与を計算したい場合:
class FullTimeEmployee(Employee):
def get_salary(self):
base_salary = super().get_salary()
return base_salary * 5 # 500%
class PartTimeEmployee(Employee):
def get_salary(self):
base_salary = super().get_salary()
return base_salary * 3 # 300%
8.4 メソッドのオーバーロード
Pythonのメソッドオーバーロード(method overloading)
は、同じ名前で異なるパラメータのメソッドを複数作成できる能力なんだ。ただし、C++やJavaなどの他の言語のように、Pythonでは純粋な形でのメソッドのオーバーロードはサポートされてないよ。
Pythonでは、デフォルト引数、*args
、**kwargs
の使用を通じて、メソッドのオーバーロードをエミュレートできるんだ。
メソッドオーバーロードの例
渡された引数の数に応じて異なる動作を持つ関数を実装することができる。これはいろんな方法で実現できるよ。例えばこんな風に:
class Example:
def display(self, a=None, b=None):
if a is not None and b is not None:
print(a, b)
elif a is not None:
print(a)
else:
print("引数がないよ")
obj = Example()
obj.display(1, 2) # 出力: 1 2
obj.display(1) # 出力: 1
obj.display() # 出力: 引数がないよ
また、渡されたデータのタイプを考慮することもできる — 型チェックをするだけでOKだよ:
class Example:
def mod(self, a, b):
if type(a) == int and type(b) == int:
print(a % b)
elif type(a) == float or type(b) == float:
print(round(a / b))
else:
print("ヘルプ: aとbはintかfloatであるべきだよ")
obj = Example()
obj.mod(5, 2) # 出力: 1
obj.mod(5.0, 2) # 出力: 2
obj.mod("5", 2) # 出力: ヘルプ: aとbはintかfloatであるべきだよ
不正なデータ型が渡された場合、説明書やインターネット上のドキュメントへのリンクを出すこともできるよ — これも非常に人気な解決策だね。
GO TO FULL VERSION