はじめに
を見て、たしかにわかりにくいよな~って思って下書きにしていたけどそのままお蔵入りになっていたもの。血筋とギプスにたとえて、すっと読めるような短い記事にしておこう
- 血筋
- ギプス
「血筋」という言い回しは「親のリソースを活用する」というニュアンスがある。「◯◯家に伝わる日本刀は、一子相伝であればこそ扱える」という感じ。
いっぽうで「ギプス」という言い回しは「砕けた骨の回復を待つために固定する」というニュアンスがある。
別にこんなもの使わなくてもいいのだ。
ただ、何に便利なのかを知るとやはり使ってしまうのだ。それはなにかというと、「コンパイラを通して意図的にエラーを出せる」ということなのだ。つまり秩序なのだ。
これがチーム開発になるとめちゃくちゃ恩恵をうけることになる。
抽象クラス
抽象クラス(=親クラス)は子クラスの基本的な振る舞いを定義する。これはまさに"血筋"の概念に似ていて、両親(抽象クラス)が子供に特定の特性を引き継ぐ。抽象クラス(=親クラス)では、一部またはすべてのメソッドが定義されていない可能性があって、それらを子クラスで定義する必要がある。これにより、派生クラス(=子クラス)は基底クラス(=親クラス)のプロパティやメソッドを再利用すると同時に、新たな特性を持つことができる。
親が夏休みの自由研究を手伝って子供がよりよい結果を得られる。自由研究はもはや親の戦いでもある、みたいなイメージがいいだろうか(親がフィクサーであり、親のリソースを使うタイプ)
from abc import ABC, abstractmethod
# 抽象クラス (Abstract Base Class)
class AbstractClassExample(ABC):
@abstractmethod
def do_something(self):
print("Some implementation!")
# 基底クラスを継承した具体クラス
class AnotherSubclass(AbstractClassExample):
def do_something(self):
super().do_something()
print("The subclass is doing something")
# インスタンスを作成
x = AnotherSubclass()
x.do_something()
Some implementation!
The subclass is doing something
親クラスが定義だけをするパターンもある。これは、親が仕組みだけを用意したパターンだ(あの家系に生まれるとかならず首に輪っかをかけている、ような感じ)。子クラスが抽象メソッドを実装しない場合(首に輪っかをかけていない場合)、その子クラスのインスタンスを作成しようとするとPythonはTypeErrorを出す。これが「意図的なエラー」だ。
from abc import ABC, abstractmethod
class AbstractClassExample(ABC):
@abstractmethod
def do_something(self):
pass # 抽象メソッドは実装を持たない
class AnotherSubclass(AbstractClassExample):
def do_something(self):
print("The subclass is implementing the abstract method.")
x = AnotherSubclass()
x.do_something()
インターフェース
インターフェースはあるクラスが特定のメソッドを持っていることを保証するための契約(とよく言われるが、あまりわかってないうちに言われるとますます沼にハマる)。
インターフェース自体は機能を持たず、具体的な実装はそれを実装するクラスに任されている。これはまさに"ギプス"のようなもの。ギプスは壊れた骨が正しく再生すればいいように一定の形式を保持し、それが果たせれば、骨自体は成長の方法を選ぶ自由がある。同様に、インターフェースはプログラムの一部(クラスまたは他のインターフェース)が特定の動作を保証することを強制するが、その動作がどのように達成されるかは全く決めていない。
マックのハッピーセットには、「バーガー」と「飲み物」と「おもちゃ」を必ずつけてね、みたいな感じかな。ルールだよね。
from typing import Protocol
class Quackable(Protocol):
def quack(self) -> str:
pass
class Duck:
def quack(self) -> str:
return "Quack!"
class Dog:
def quack(self) -> str:
return "Woof!"
# ここで animal が Quackable で定義したメソッドを持っているか?をチェックする
def make_it_quack(animal: Quackable) -> None:
print(animal.quack())
duck = Duck()
dog = Dog()
make_it_quack(duck) # Output: Quack!
make_it_quack(dog) # Output: Woof!