2024年11月: 型ヒントの動向と新しい機能の紹介¶
鈴木たかのり(@takanory)です。 今月の「Python Monthly Topics」では、Pythonの型ヒントの最近の動き、比較的新しい型ヒントの機能について紹介します。
本連載でも過去にいくつも型ヒント関連の記事があります。 このようによりよいPythonコードを書くための型ヒントが、Pythonバージョンの更新に伴って追加されています。
2023年1月:O/Rマッパーの型チェックを強化できるPython 3.11の新機能 Data Class Transforms(PEP 681) | gihyo.jp
2023年5月:Python 3.11の新機能:型チェッカーでロジックの間違いを検出できるtyping.assert_never関数とtyping.Never型 | gihyo.jp
2023年9月:Python 3.12の新機能「PEP 692: Using TypedDict for more precise **kwargs typing」の紹介 | gihyo.jp
PEP 729 – Typing governance process:型ヒントのガバナンス¶
今回調べて知ったんですが、以下のドキュメント(PEP 729)で型ヒントのガバナンス(運営管理)方法について提案されており、2023年11月に採択されました。
PEP 729では型ヒントの保守、開発を行うPython型ヒント評議会(Python Typing Council)を立ち上げることが提案されています。 Pythonの言語仕様はPEP(Python Enahncement Proposal:Python拡張提案)というドキュメントで提案され、その提案が採択されることで決定されます。 PEPを採択するかの判断はPython Steering Council(以下SC)が行っており、SCは5名の評議委員で構成されています。 しかし、SCはPythonのすべてのPEPに対して検討・判断を行うため「型ヒントのPEPに対して適切な判断をすることが難しい」ということがPEP 729で主張されています。
最初に書いたとおりPEP 729はSCによって2023年11月に採択され、現在5名の初期メンバーがSCにより任命されています。 5名のメンバーは、以下のように型ヒントや型関連のツールに習熟した開発者が選ばれています。
現在のメンバーは以下のリポジトリで記録されています。 メンバー数は3〜5名で最長連続5年などの制限があります。
評議会はその取り組みの一部として、以下のこと行います。
仕様に準拠したテストスイートの作成
型システムの仕様の作成
型システムのユーザー向けリファレンスの作成
なお、Python Steering Councilでの運営は、Guido van Rossum氏がBDFLから引退したことを受け、2019年から始まっています。 SCについて詳細を知りたい方は以下の参考資料を確認してください。
型ヒントのドキュメント¶
上記の評議会の成果の一つとして、型ヒント、型システムを使用するユーザー向けのリファレンスドキュメントが以下のサイト「Static Typing with Python」で公開されています。

Static Typing with Python¶
いままで型ヒントについてはPEPのみが仕様書で、PEPドキュメント自体は仕様を議論するためのドキュメントのため、ユーザー用のドキュメントとしては適切ではありません。 そこで、型システムについてのユーザー向けドキュメントが作成されました。 このドキュメントでは型システムのガイド、リファレンス、仕様が書かれています。 また、型関連ツールが紹介されています。
仕様(Specification)のセクションには、各種型ヒントの仕様や使い方が記載されています。 以降で説明する新しい型ヒントの機能についても、元となったPEPのドキュメントから、型ヒントのドキュメントへの参照が追加されています。

「Attention」の中で型ヒントのドキュメントを参照している¶
型ヒントの公式ドキュメントは以下のリポジトリで管理されています。
@override
デコレーター¶
Python公式ドキュメント:@typing.override
型ヒントドキュメント:@override
PEP:PEP 698 – Override Decorator for Static Typing | peps.python.org
Python 3.12でtypingモジュールに@override
というデコレーターが追加されました。
このデコレーターはクラスを継承したときに、サブクラスがスーパークラスの属性やメソッドをオーバーライドしていることを表します。 もし、このデコレーターが付いている属性やメソッドが、実際にはなにもオーバーライドしていない場合は、型チェッカーはエラーを出力します。 こうすることで「オーバーライドしたつもりだけど、実はオーバーライドしていない」というミスを防ぎます。
以下のサンプルコードではPet
を継承したFerret
サブクラスを作成しています。
サブクラスでは2つのメソッドを定義して、両方に@override
デコレーターを指定しています。
from typing import override
class Pet:
"""ペットを表すクラス"""
name: str
def sleep(self) -> None:
print(f"{self.name}-chan is sleeping.")
def eat(self) -> None:
print(f"{self.name}-chan is eating.")
class Ferret(Pet):
"""フェレットを表すクラス"""
@override
def sleep(self) -> None: # OK
print(f"Ferret, {self.name}-chan is sleeping.")
@override
def eet(self) -> None: # NG
print(f"Ferret, {self.name}-chan is eating.")
pyrightでこのコードをチェックすると「eet
メソッドにoverride
マークが付いているが、ベースメソッドが存在していない」というエラーが発生します。
% pyright override_sample.py
/.../override_sample.py
/.../override_sample.py:22:9 - error: Method "eet" is marked as override, but no base method of same name is present (reportGeneralTypeIssues)
1 error, 0 warnings, 0 informations
このエラーは、サブクラスで本来eat
と書くべき所をeet
と間違えているために発生しています。
この間違いに気がつかないとFerret.eat()
を呼び出した時に、親クラスのメソッドが呼び出されてしまいます。
@override
を書くことによってpyright、mypyなどの型チェッカーでチェックできるようになります。
TypedDict
でRequired
とNotRequired
を使用する¶
Python公式ドキュメント:typing.Required
型ヒントドキュメント:Required and NotRequired
PEP:PEP 655 – Marking individual TypedDict items as required or potentially-missing | peps.python.org/
Python 3.11でtypingモジュールにRequired
とNotRequired
が追加されました。
この2つの型ヒントは、TypedDict
と組み合わせ使用します。
TypedDict
で辞書の各キーに対して型ヒントを定義し、各キーに対して必須、非必須を指定できます。
TypedDict
の基本¶
まずはTypedDict
の動作を確認します。
以下のコードでは辞書の3つのキー(name
、farm
、age
)に対して、型ヒントでstr
やint
を指定しています。
from typing import TypedDict
class Ferret(TypedDict):
"""フェレットを表す辞書"""
name: str
farm: str
age: int
guri1: Ferret = {"name": "guri", "farm": "Canadian", "age": 5} # OK
guri2: Ferret = {"name": "guri", "farm": "Canadian", "age": "five"} # NG
guri3: Ferret = {"name": "guri", "age": 5} # NG
このコードをpyrightでチェックすると、2番目と3番目のパターンでエラーが発生します。
12行目はage
にはint
を指定すべきところをstr
を指定しているためにエラーになっています。
13行目はfarm
キーが指定されていないためにエラーとなっています。
% pyright typeddict_sample1.py
/.../typeddict_sample1.py
/.../typeddict_sample1.py:12:61 - error: Type "dict[str, str]" is not assignable to declared type "Ferret"
"Literal['five']" is not assignable to "int" (reportAssignmentType)
/.../typeddict_sample1.py:13:17 - error: Type "dict[str, str | int]" is not assignable to declared type "Ferret"
"farm" is required in "Ferret" (reportAssignmentType)
2 errors, 0 warnings, 0 informations
NotRequired
を使用¶
farm
キーを非必須にするためにNotRequired
を使用します。
型ヒントにNotRequired[str]
と書くことで、このキーが非必須(オプション)となります。
from typing import NotRequired, TypedDict
class Ferret(TypedDict):
"""フェレットを表す辞書"""
name: str
farm: NotRequired[str] # farmを非必須にする
age: int
gura1: Ferret = {"name": "gura", "farm": "Path Valley", "age": 6} # OK
gura2: Ferret = {"name": "gura", "farm": "Path Valley", "age": "six"} # NG
gura3: Ferret = {"name": "gura", "age": 6} # OK
このコードをpyrightでチェックすると、farm
キーを指定していない13行目がエラーではなくなります。
% pyright typeddict_sample2.py
/.../typeddict_sample2.py
/.../typeddict_sample2.py:12:64 - error: Type "dict[str, str]" is not assignable to declared type "Ferret"
"Literal['six']" is not assignable to "int" (reportAssignmentType)
1 error, 0 warnings, 0 informations
Required
を使用¶
TypedDict
でtotal=False
と指定するとすべてのキーが非必須となります。
その場合、逆にRequired
を使用して必須に指定できます。
以下のコード例ではname
とage
のみを必須としています。
from typing import Required, TypedDict
class Ferret(TypedDict, total=False):
"""フェレットを表す辞書"""
name: Required[str]
farm: str
color: str
age: Required[int]
seven1: Ferret = {"name": "seven", "farm": "Far Farm", "age": 6} # OK
seven2: Ferret = {"name": "seven", "color": "Black Self", "age": 6} # OK
seven3: Ferret = {"name": "seven", "age": 6} # OK
seven3: Ferret = {"name": "seven", "color": "Black Self"} # NG
上記のコードをpyrightでチェックすると、最後のパターンで必須(Required
)の要素age
が指定されていないためエラーとなります。
% pyright typeddict_sample3.py
/.../typeddict_sample3.py
/.../typeddict_sample3.py:15:18 - error: Type "dict[str, str]" is not assignable to declared type "Ferret"
"age" is required in "Ferret" (reportAssignmentType)
1 error, 0 warnings, 0 informations
任意の文字列リテラル型¶
Python公式ドキュメント:typing.LiteralString
型ヒントドキュメント:LiteralString
PEP:PEP 675 – Arbitrary Literal String Type | peps.python.org
Python 3.11でtypingモジュールにLiteralString
が追加されました。
この型ヒントは文字列リテラルのみを表します。
str
型ではなく、文字列リテラルで作成した文字列のみで構成されたや文字列のみが使用できます。
なおLiteralString
は型チェックにのみに使用される特別な形式でデータ型としては存在しません。
from typing import LiteralString
from pathlib import Path
def eat(name: LiteralString) -> None:
"""LiteralString型のnameのみを受け取る"""
print(f"{name}-chan is eating.")
eat("seven") # OK
name = "seven"
eat(name) # OK
eat(name.title()) # OK
eat("Ferret, " + name) # OK
name = input()
eat(name) # NG
eat(Path("name.txt").read_text()) # NG
上記のコードをpyrightでチェックすると、input()
で入力を受け取る場合とファイルから文字列を取得する場合にLiteralString
ではなくstr
となるためエラーが発生します。
.title()
で文字列を変換する場合や+
演算子で文字列を連結していても、元となる文字列がLiteralString
型の場合は変換された文字列もLiteralString
となるためエラーとなりません。
% pyright literalstring_sample.py
/..;/literalstring_sample.py
/.../literalstring_sample.py:16:5 - error: Argument of type "str" cannot be assigned to parameter "name" of type "LiteralString" in function "eat"
"str" is not assignable to "LiteralString" (reportArgumentType)
/.../literalstring_sample.py:17:5 - error: Argument of type "str" cannot be assigned to parameter "name" of type "LiteralString" in function "eat"
"str" is not assignable to "LiteralString" (reportArgumentType)
2 errors, 0 warnings, 0 informations
この機能は、たとえばプログラム中で実行するSQLやシェルのコマンドに、ユーザーが入力したデータが混入することを防ぐといった用途に使用できます。
以下のコード例では、最後の2つのパターンはLiteralString
ではないため、型チェックを実行するとエラーとなります。
from typing import LiteralString
def run_query(sql: LiteralString) -> None:
...
def caller(arbitrary: str, literal: LiteralString) -> None:
run_query("SELECT * FROM animals") # OK
run_query(literal) # OK
run_query("SELECT * FROM " + literal) # OK
run_query(f"SELECT * FROM {literal}") # OK
run_query(arbitrary) # NG
run_query(f"SELECT * FROM animals WHERE name = {arbitrary}") # NG
まとめ¶
本記事では、最近の型ヒントの動きとして以下を紹介しました。
PEP 729 – Typing governance processが採択され「Python型ヒント評議会」が発足したこと
型ヒントのドキュメント「Static Typing with Python」がhttps://typing.readthedocs.io/で公開されていること
また、最近追加された以下の型ヒントを紹介しました。
オーバーライドしたメソッドに指定する
@override
デコレーターTypedDict
の必須、非必須のキーを指定するRequired
、NotRequired
文字列リテラルのみを含む
LiteralString
評議会による運営により、今後も継続的に型システムに関する機能が追加・改善していくと思われます。 どのような機能が出てくるのか楽しみです。