フリーキーズ | 独学プログラミング

PythonのGenericsを使いこなそう

最終更新日
【画像】PythonのGenericsを使いこなそう

Python 3.12のGenerics(ジェネリクス)の書き方がクリーンになった記憶はまだ新しいです。
ですが、実際にPythonプロジェクトでGenericsを使っていないケースも多く見受けられます。「使わなくても実装できる」という前提はあるものの、Genericsはより汎用的かつ実用的なコードを書く際にとても便利です。

このコンテンツでは、「PythonのGenericsってなんぞや」という人向けにPythonのGenericsについて簡単に紹介します。

そもそもGenerics(ジェネリクス)とは

Genericsとは、汎用型(または総称型)と呼ばれる汎用的な型に対応できるクラスや関数を作れる機能のことを指します。TypeScriptやJava、最近ではRustで馴染みのある機能です。

わかりやすく一旦TypeScriptで書くと、以下のような形になります。

function get_str(val: string): string {
  return val
}

function get_num(val: number): number {
  return val
}

console.log(get_str('Hello World!')) //-> Hello World!
console.log(get_num(42)) //-> 42

どちらの関数も同じく引数のvalを返すだけですが、型が違うことによって別々の関数として定義しなくてはいけません。これを汎用的な関数にしたいため、Genericsを利用して書き直します。

function get_val<T>(val: T): T {
    return val
}

console.log(get_val<string>('Hello World!')) //-> Hello World!
console.log(get_val<number>(42)) //-> 42

このように、Genericsを使うと関数実行時に型を定める書き方ができるようになります。

Genericsを使うメリットと注意点

なぜGenericsを使うと嬉しいか、いくつかのポイントがあります。以下がGenericsを使うメリットとなります。

  • 省コード化に役立つ
  • テストが減る(クラスや関数ごとに型でfailするテストケースを書かなくていい)
  • TypeScriptでいうany、Pythonでいうtyping.Anyの撲滅に貢献

linterでコードチェックしているときに引っかかる問題へ役立ちます。

ただし、注意したほうが良いのは、正しい用法を理解した上で利用することです。
複数の型に対応できるということで、あやふやコード設計の上でGenericsを使い倒してしまうと、最終的に型安全ではないコードが量産されてしまいます。

それゆえ、Genericsは限定的な使い方をするのをおすすめします。

PythonのGenericsの簡単な構文

ここで本題となりますが、PythonによるGenerics構文を見ていきましょう。

まずは、Python 3.11以前の書き方を紹介します。

from typing import TypeVar


T = TypeVar('T', int, float, complex)

def f(x: T) -> T:
    return x

print(f(27))

見て分かる通り、冗長な書き方をするしか方法がありませんでした。
これがPython 3.12で以下のように書けるようになりました。

def get_val[T](x: T) -> T:
    return x

print(get_val(27))
class A[T]:
    def get_val(self, x: T) -> T:
        return x

a = A[int]()
print(a.get_val(27))

このように、現行最新バージョンのPythonでは、TypeScriptなどに近い書き方が可能です。

Python記法の進化

Pythonは静的型付け言語ではないため、真のGenericsと呼べるかは微妙なラインです。しかしながら、タイプヒンティングをより取り入れたコーディングのために、新たな記法が生まれてくれたおかげでPythonらしさが保たれたように感じます。

Pythonコーディングに慣れていると、とっつきにくいと感じやすいですが、便利な機能なのでぜひ導入してみてください。

関連するコンテンツ