ポリモーフィズムとは?
プログラミングにおけるポリモーフィズム(多態性)とは、関数呼び出しやメソッド名のような1つのコードが、異なる型の値に対して働き、それぞれの型が自分自身の振る舞いを提供できる性質のことです。shape.area() を呼ぶと、円と正方形では別の計算が実行されます。
更新日: 2026年9月24日
Pythonでは、len("hello") は5を、len([4, 8, 15]) は3を、len({"a": 1}) は1を返します。1つの関数名を3つの型に使っていて、それぞれの型が独自の方法で数えています。文字列は文字を、リストは要素を、辞書はキーを数えます。この言葉はギリシャ語の poly(多くの)と morph(形)から来ています。1つの呼び出しに、多くの形の振る舞いがあるということです。
ポリモーフィズムの仕組み
コードは shape.area() のように、名前でメソッドを呼び出します。その行が実行されるとき、言語は実際のオブジェクトを見て、そのオブジェクト自身のクラスから area を探します。この探索を動的ディスパッチと呼び、これによって1行のコードがオブジェクトごとに違う仕事をできるのです。
Circle 3.14
Square 9
Circle 12.57
Pythonは、まずオブジェクトのクラスを探し、次にメソッド解決順序(MRO)と呼ばれる決まった順番で親クラスを探して、メソッドを見つけます。もし Square に独自の area() がなければ、探索は Shape.area() に到達して NotImplementedError を発生させます。これは、すべてのサブクラスが独自のメソッドを用意しなければならないという合図です。
ループは、自分が持っている図形の種類を一度もチェックしていません。三角形を追加するには、独自の area() を持つ Triangle クラスを書くだけで、ループはそのまま動きます。ポリモーフィズムがなければ、ループには図形ごとに if isinstance(...) の分岐が必要になり、新しい図形が現れるたびに修正しなければなりません。
ポリモーフィズムの種類
| 種類 | 別名 | どのように見えるか | どのバージョンを使うかが決まるタイミング |
|---|---|---|---|
| サブタイプ | 実行時ポリモーフィズム、メソッドのオーバーライド | サブクラスが親のメソッドを置き換える | プログラムの実行中 |
| アドホック | オーバーロード | 1つの名前に、引数の型ごとに複数のバージョンがある。演算子のオーバーロードも含む | JavaとC++ではコンパイル時 |
| パラメトリック | ジェネリクス、テンプレート | どんな型にも使える1つのコード。Javaの List<T>、C++の vector<T> など | コンパイル時 |
オブジェクト指向プログラミングで、詳しい説明なしに「ポリモーフィズム」と言う場合、ほとんど必ず1行目、つまりオーバーライドによるサブタイプのポリモーフィズムを指しています。
Pythonには、独自の形でこの3つすべてがあります。オーバーライドは図形の例のとおりに働きます。+ のような演算子はアドホックなポリモーフィズムです。max() や sorted() のような関数は、考え方としてはパラメトリックです。要素どうしを比較できる限り、数、文字列、日付のどのリストでも受け付けますし、list[int] や TypeVar のような型ヒントでそれを明示することもできます。
Javaのコンパイル時ポリモーフィズムと実行時ポリモーフィズム
Javaには両方の種類があり、面接でもその違いがよく質問されます。
class Animal { String sound() { return "..."; } }
class Dog extends Animal {
@Override String sound() { return "Woof"; }
}
Animal pet = new Dog();
pet.sound(); // "Woof": chosen at run time from the object
int add(int a, int b) { return a + b; }
double add(double a, double b) { return a + b; }
add(2, 3); // int version, chosen by the compiler
add(2.5, 1.0); // double version
変数 pet の型は Animal なのに、実行されるのは Dog のバージョンです。どれを選ぶかは変数ではなくオブジェクトによって決まるからです。2つの add メソッドはオーバーロードで、コンパイラがプログラムの実行前に、引数の型からどちらかを選びます。インタフェースを使えば、コードを共有せずに同じ実行時の振る舞いが得られます。Comparable を実装したクラスなら、どれでも Collections.sort で並べ替えられます。C++も同じように動きますが、1つだけ違いがあります。メソッドが実行時にディスパッチされるのは、virtual と宣言されている場合だけです。Javaのポリモーフィズム、メソッドのオーバーロード、C++の仮想関数を参照してください。
Pythonのポリモーフィズム: ダックタイピングと演算子
Pythonは共通の親クラスを必要としません。コードが呼び出すメソッドをオブジェクトが持っていれば、それで動きます。「アヒルのように歩き、アヒルのように鳴くなら、それはアヒルだ」という考え方です。演算子もポリモーフィックです。a + b と書くと a.__add__(b) が呼ばれるので、自分で作ったクラスでも +、len()、print() に対応させられます。
3 abcd [1, 2]
Vector(11, 22)
5 3 2
継承はポリモーフィズムを実現する方法の1つであって、必須ではありません。Vector は特別なものを何も継承していないのに、len() と + はそれを組み込み型と同じように扱います。サブクラスを使う方法はPythonの継承のドキュメントで説明しています。
よくある間違い
Pythonがシグネチャでオーバーロードしてくれると期待する。 同じ名前で2つ目の def を書くと、1つ目が何の警告もなく置き換えられます。
Hi Ana Silva
Traceback (most recent call last):
...
TypeError: greet() missing 1 required positional argument: 'last'
代わりにデフォルト引数を使ってください(def greet(first, last=""))。
C++で virtual を付け忘れる。 これがないと、基底クラスへのポインタを通してメソッドを呼んだとき、オブジェクトがサブクラスであっても基底クラスのバージョンが実行されます。
意味を変えてしまうオーバーライド。 あるサブクラスの area() が周囲の長さを返したら、area() を信頼しているすべての呼び出し側が壊れます。オーバーライドは親がした約束を守らなければなりません。このルールはリスコフの置換原則として知られています。
あちこちでの型チェック。 if type(x) == ... の分岐が連なっているのは、たいてい、それぞれの型が自分自身のメソッドを持つべきだというサインです。
次に読むページ
ポリモーフィズムは、各オブジェクトのデータを守るカプセル化や、型が実装する共通のインタフェースを定める抽象化と一緒に働きます。オーバーライドはPythonのクラスのドキュメントで練習できます。オーバーロードとオーバーライドが並んで登場するJavaコースにも挑戦してみてください。
よくある質問
ポリモーフィズムの具体例は?
+ 演算子です。1 + 2 は数を足し、"ab" + "cd" は文字列をつなぎ、[1] + [2] はリストをつなぎます。同じ記号が3つの型で働き、それが何を意味するかはそれぞれの型が決めます。オブジェクト指向のコードでは、図形のリストのそれぞれが自分の公式で area() に答える、というのが典型的な例です。ポリモーフィズムと継承の違いは?
生物学でのポリモーフィズム(多型)とは?
Pythonはメソッドのオーバーロードに対応していますか?
def を書くと、1つ目が置き換えられます。Pythonのコードでは、デフォルト引数、*args、あるいは最初の引数の型に応じて関数を選ぶ functools.singledispatch で同じ効果を得ます。ポリモーフィズムはなぜ役に立つのですか?
shape.draw() を呼ぶお絵かきプログラムは、三角形のクラスを追加しても修正する必要がなく、新しいクラスを書くだけで済みます。ポリモーフィズムがなければ、そのループには型ごとに新しい if の分岐が増えていくことになります。