mizzsugar’s blog

日々感じていることや学んだことを書きます。エンジニアリング以外にも書くかもしれません。

PythonとTypeScriptで学ぶGenerics初めの一歩

TwitterGenericsの話が浮上しているのに影響されて、Generics使うと何が良いのか落とし込みました。

なお、この2つの言語にしたのは、普段自分が使うからです。

Genericsとは

「総称型」とか「汎用型」と言われます。 型定義にGenericsを使うことで文字列型や数値型など具体的な型に依存しない 抽象的かつ汎用的な関数やクラスを作ることができます。

そもそも、「generic」という単語は「汎用の」という意味があります。 そこから、「Generics」とは汎用的な何かを指しているんだなと想像できます。

Pythonでの定義の仕方

sample.py

from typing import TypeVar, Sequence

T = TypeVar(’T’)  # TypeVarを使ってTという名前の型だと宣言します。

def fist(l: Sequence[T]) -> T:  # 渡された配列の一番最初の要素を返す関数
    return l[0]

TypeScriptでの定義の仕方

sample.ts

function first<T> (l: T[]): T {
  return l[0]
}

サンプルコードはPythonのドキュメントから。

typing --- 型ヒントのサポート — Python 3.8.2 ドキュメント

Genericsを使わないとどうなる?

最初の要素を返す関数をGenericsを使わないで書いてみましょう。

実装し始めた頃は要素が文字列の場合のみ想定されていたとします。

Python

sample.py

from typing import Sequence


def fist(l: Sequence[str]) -> str:
    return l[0]

Typescript

sample.ts

function first(l: string[]): string {
  return l[0]
}

後々、数値やオブジェクトにも使いたいという要望が出ます。

Python

from typing import Sequence

from item import Item   // 自作クラス


def fist_str(l: Sequence[str]) -> str:
    return l[0]

def fist_int(l: Sequence[int]) -> int:
    return l[0]

def fist_item(l: Sequence[Item]) -> Item:
    return l[0]

TypeScript

import { Item } from "@/src/item"  // 自作クラス


function first_string(l: string[]): string {
  return l[0]
}

function first_number(l: number[]): number {
  return l[0]
}

function first_item(l: Item[]): Item {
  return l[0]
}

これだと、型だけ異なる同じ内容の処理の関数が型の数だけ書かないといけなくなります。

色んな型で使いたい、けどAnyではなくその型じゃないといけないよと宣言したい処理を書く時にGenericsは便利だと思いました。

例えばこれだと、lが数値の配列である時に返り値が文字列でも数値でも真偽値でもなんでもOKになってしまいます。

Python

from typing import Any, Sequence


def fist_item(l: Sequence[Any]) -> Any:
    return l[0]

TypeScript

function first (l: any[]): any {
  return l[0]
}

Genericsがないと汎用的なライブラリやフレームワークの実装で型の数だけ関数やクラスを書いているとだいぶ辛いそうだなと想像しました。

辛くならないようにGenericsを使いこなせるようになりたいなと思います。