1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[初心者向け] Python のオーバーロード

Last updated at Posted at 2025-04-05

はじめに

Python におけるオーバーロードを解説していきます。
@overload@singledispatch のどちらかで解説されているものが多く混乱しやすかったので、両方の性質を比較して整理していきます。

オーバーライドはオブジェクト指向の回で解説する予定なので、興味あればそちらもご覧ください。

型チェッカーを有効にしないと、解説の恩恵を受けられません

オーバーロード

引数が異なる同名の関数 (メソッド) や演算子を定義することです。多重定義ともいいます。

Java でのオーバーロードは、引数の構成を変えた同名の関数を定義するだけです。

Java でのオーバーロード
public static int func(int x, int y){
    return x + y;
}

public static int func(int x, int y, int z){
    return x + y + z;
}

// 以下問題なく呼び出せる
func(1, 2);
func(1, 2, 3);

Python では同名の関数を定義すると、前に定義したものの情報が上書きされて消えてしまいます。

Python でも似たようなことをやってみると...
def func(x: int, y: int) -> int:
    return x + y


def func(x: int, y: int, z: int) -> int:
    return x + y + z

# 2 つめの func が有効なので引数が 3 つ必要
func(1, 2)  # 1 つめの func の情報は存在しない
func(1, 2, 3)

Java ほど単純ではありませんが、Python にもオーバーロードのための記述があるので、それを解説していきます。

まず説明する引数の型に応じた戻り値の型は、型チェッカーに厳密性を付与するものです。
その次の引数の型に応じた処理の分岐が、一般的なオーバーロードのイメージに近いです。

引数の型に応じた戻り値の型

引数の型によって、戻り値の型が変わる処理を考えます。

下では関数の引数は int または str で、戻り値の型は引数の型と同じです。
最終行において int_or_str の引数が int なので、その戻り値も同じ int となり、問題ないように思えます。
ただし型チェッカーは内部の処理までは見てくれず、関数定義時の型のみで判断するため、この記述では戻り値の型は int | str でなければならないと表示されます。

引数によって戻り値の異なる関数
def int_or_str(arg: int | str) -> int | str:
    """引数が int であれば倍に、str であれば繰り返す"""
    return arg * 2

val_1:int = int_or_str(1)  # 型チェッカーによるエラーが表示

overload デコレータ

@overload を利用すると、引数と戻り値の型を正確に紐づけられます。

引数の型と戻り値の型の組み合わせの記述
from typing import overload


@overload
def func(arg: tp_1) -> tp_a:
    pass


@overload
def func(arg: tp_2) -> tp_b:
    pass


def func(arg: Union[tp_1, tp_2]):
    ...

@overload でのオーバーロードを定義する手順は以下の通りです。

  1. 複数の引数の型を取りうる関数を定義
  2. 1 の関数の上に @overload を付与した同名の関数を定義
  3. 2 の関数の引数と戻り値に型ヒントを付与して、pass と記述
  4. 紐づけたい引数と戻り値の型の組み合わせの数だけ 2 ~ 3 を繰り返す

さきほどの例を @overload を利用して記述すると、以下のようになります。

正確な引数の型と戻り値の型の紐づけ
@overload
def int_or_str(arg: int) -> int:
    pass


@overload
def int_or_str(arg: str) -> str:
    pass


def int_or_str(arg: int | str) -> int | str:
    return arg * 2


# 引数の型から戻り値の型が正確に推定
val_2: int = int_or_str(1)  # 戻り値は int 型
val_3: str = int_or_str("a")  # 戻り値は str 型

@overload が付与されている関数は、型チェッカーのために利用されるもので、中の処理は無視されます。
つまり型チェッカーを黙らせる機能でしかないため、引数の型によって処理を変えるようなことはできません。

引数の型に応じた処理を行うには @singledispatch を利用します。

引数の型に応じた処理の分岐

引数の型によって処理を分岐させるケースを考えます。

下では引数が intstr で、処理を変えています。型の判定のためだけに if を使用しており、可読性が微妙です。

原始的な引数の型に応じた処理の分岐
def twins(arg: int | str):
    """引数の型によって異なる処理"""
    if type(arg) is int:
        # 年齢を倍にして表示
        print(f"age : {arg*2}")
    elif type(arg) is str:
        # 名前を繰り返し表示
        print(f"name : {arg*2}")


twins(28)
twins("ABC")

--> age : 56
    name : ABCABC

singledispatch デコレータ

@singledispatch を利用すると、内部の処理がスッキリして可読性が向上します。

引数の型に応じた処理の分岐
from functools import singledispatch


@singledispatch
def func(arg: tp_1):
    A


@func.register
def _(arg: tp_2):
    B

@singledispatch でのオーバーロードの手順は以下の通りです。

  1. @singledispatch を付与した関数を型ヒントも記述して定義
  2. 関数名と .registerから成るデコレータを付与して、関数名を _ で定義
  3. 2 の関数に型ヒントと引数の型に応じた処理を記述
  4. 引数の型とそれに紐づいた処理の組み合わせの数だけ 2 ~ 3 を繰り返す

さきほどの例を singledispatch を利用して記述すると、以下のようになります。

可読性の高い引数の型に応じた処理の分岐
@singledispatch
def twins(age: int):
    print(f"age : {age * 2}")


@twins.register
def _(name: str):
    print(f"name : {name * 2}")


twins(28)  # 引数が int → 上の関数
twins("ABC")  # 引数が str → 下の関数

@singledispatch のほうが、汎用的に思えます。

補足

今回のケースでは引数と戻り値の型が同一であるため、ジェネリクスでも代用できます。いずれジェネリクスも解説します。

まとめ

型チェッカーを黙らせる場合は @overload、処理を変える場合は @singledispatch を利用してください。

おわりに

年明け転職しまして、かなり忙しくなりました。4 月に入りようやく落ち着いたので、記事の投稿を再開します。

更新履歴

2025/04/05 初版
2025/04/24 違いをより明確にするような説明を追加

1
1
0

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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?