はじめに
Pythonは柔軟性と書きやすさ・読みやすさから開発者に人気なプログラミング言語の1つです。
しかし、動的型付け言語であるため、コードが大規模化してくると型に関連するバグや可読性低下などの問題が発生してきます。
こういった型起因の問題を軽減するために役立つのが型ヒント(Type Hint)です。
型ヒントはPython3.5から導入された機能で、コードに型情報を付与することでデータの型を明確するための仕組みです。
静的解析ツールやIDEの機能を使ってバグを早期に発見することができますし、型情報が付与されるため可読性も高まります。
特に近年は型ヒントが進化し、より直感的な記述ができるようになってきています。
本記事では、Python3.12で追加された型ヒントの新機能についていくつか紹介します。
Python3.12で追加された新機能の紹介
1. type構文を使った型エイリアス定義
型エイリアスとは
型エイリアスとは、型情報に名前をつけることです。型エイリアスをつかうことでコードの可読性と再利用性の向上が期待できます。
下記は型エイリアスの基本的な記述例です
変数に型情報を代入します。
# 数値の型エイリアス
Number = int | float
# 座標の型エイリアス
Coordinates = list[tuple(float, float))
⭐️type構文(type statement)
しかし、上記の記述では変数と型エイリアスの判別はぱっと見わかりづらいです。
そこで、Python3.12で追加されたtype構文(type statement)を使って型エイリアスの定義を明示します。
記述は単純に型エイリアス定義の前にtype
をつけるだけです。
type Number = int | float
type Coordinates = list[tuple(float, float))
type構文により型エイリアスの定義であることがわかりやすくなりました。
Python3.10で型エイリアスを明示するためのtyping.TypeAlias
が追加されましたが、type文の導入によりtyping.TypeAlias
は非推奨になりました。
from typing import TypeAlias
Number: TypeAlias = int | float
2. 型パラメータ構文を使ったジェネリクス
ジェネリクスとは
ジェネリクスは型に依存しない汎用的なコードを記述するための仕組みです。
特定の型に依存せず、さまざまな型を扱う柔軟なクラスや関数を作成することができます。
型変数とは
型変数はジェネリクスを実現するための部品です。
型変数を利用して入力型と出力型が関連付けられた関数やクラスを作成することができます。
typing
モジュールのTypeVar
を使用して型変数T
を定義します。
from typing import TypeVar
T = TypeVar("T")
ジェネリクスの実装例
下記はPython3.12より前の記述例です
関数の例
下記は型変数を使ったジェネリック(汎用的)な関数の例です。
double_value
関数の引数value
と返り値の型に型変数T
を指定します。
from typing import TypeVar
T = TypeVar("T")
def double_value(value: T) -> T:
return value * 2
get_value(123) # => 246
get_value("hello") # => "hellohello"
クラスの例
クラスの場合はtyping
モジュールのGeneric
を使用して下記のように記述します。
from typing import Generic, TypeVar
T = TypeVar("T")
class A(Generic[T]):
def __init__(self, value: T) -> None:
self.value = value
def get_value(self) -> T:
return self.value
⭐️型パラメータ構文(type parameter statement)
Python3.12で型パラメータ構文(type parameter statement)が追加されました。
以前は、上述の通りTypeVar
を使用して型変数を定義していましたが、型パラメータ構文の導入によりTypeVar
を使用せず型変数を定義できるようになりました。
例の関数とクラスを型パラメータ構文で書き換えた記述を下記に示します。
def double_value[T](value: T) -> T:
return value * 2
class A[T]:
def __init__(self, value: T) -> None:
self.value = value
def get_value(self) -> T:
return self.value
TypeVar
を使ったジェネリクスの記述よりもシンプルになりました。
型変数T
をグローバル宣言する必要がなくなり、関数・クラス内で完結しているのもいいと思います。
3. メソッドのオーバーライド
⭐️@override
デコレーター
Python3.12でtyping.override
が追加されました。
継承元のメソッドを上書きしていることを明示する機能です。
メソッドのオーバーライドすることを表す機能があればなぁと思ってたところ追加されました。
使い方はオーバーライドするメソッドに@override
デコレーターを付与するだけです。
from typing import override
class Parent:
def run(self) -> None:
print("Base")
class Child(Parent):
@override
def run(self) -> None:
print("Child")
単純な例なのであまり有用性を感じないかもしれませんが、規模が大きくなってくると子クラス固有のメソッドとオーバーライドしたメソッドの分別が難しく可読性の低いコードになります。
@override
を使用することでメソッドがオーバーライドされていることが明示されて可読性が高まります。
まとめ
Python3.12で追加された新機能について紹介しました。
単純なコードであれば型ヒントがなくても十分だと思いますが、コードの規模が大きくなると型起因の問題の発生が考えられます。
規模が大きく型ヒントを利用されてない場合、入力と出力が分かりづらくコードを読むのが大変だと思ったことがあります。
型ヒントを取り入れて、堅牢で保守性の高いコードを目指しましょう!
おわりに
ここまで読んでいただきありがとうございます!
引き続きアドベントカレンダー続きますのでよければ明日以降の記事もご覧ください!
もし弊社インティメート・マージャーに興味があればご覧ください!