型ヒント関連の新機能はまだ他にもありますが、ここでは以下の機能を紹介します。
TypedDict と NamedTuple で Generic を定義可能に¶
Python 3.10までは、TypedDict と NamedTuple を定義するとき、派生元として Genericなど、他の型を指定できませんでした。
このため、TypedDict
と NamedTuple
をジェネリックスとして使えなかったのですが、Python3.11ではこの制限が緩和され、Generic
から継承できるようになりました。次のように、型変数を利用した型定義が可能です。
from typing import Generic, TypedDict, TypeVar, NamedTuple
T = TypeVar("T")
class TD(TypedDict, Generic[T]):
item: T
td1: TD[int] = {'item': 100} # OK
td2: TD[int] = {'item': "100"} # Error
td3 = TD(item=[100])
reveal_type(td3) # TD[list[int]]
class NP(NamedTuple, Generic[T]):
elem: T
np1 = NP(elem=100)
reveal_type(np1) # "NP[int]"
np2 : NP[str] = NP(elem=200) # Error
PEP-675 任意の文字列定数¶
これまでの型ヒントでは、Literal["SPAM"]
を使って、決まった値の文字列定数のみ有効、という指定ができました。しかし、内容に関わらず、文字列定数なら有効 という指定はできませんでした。
Python 3.11では PEP-675 任意の文字列定数 の LiteralString が追加され、"文字列定数ならなんでもOK"
のような、文字列定数の値のみを使えるように指定できるようになりました。
次の例では、関数 f(s: LiteralString)
は、f("SELECT * FROM emp")
のように文字列定数のみを指定できます。f(input())
のように、文字列定数以外の文字列を指定した場合は、エラーとなります。
import sys
from typing import LiteralString
def f(s: LiteralString):
database.execute(s)
f("SELECT * FROM emp") # 文字列定数なのでOK
f(input()) # 外部から入力した文字列なのでError
一般に、ファイルやネットワークなど、外部から取得した文字列は、SQLの一部としてデータベースにアクセスしたり、HTMLの一部としてWebブラウザで表示したりすると、セキュリティ上の問題が発生します。しかし、文字列定数はプログラムのソースコード中に直接書き込まれた文字列なので、外部からの攻撃に利用される可能性は低く、そのまま利用できます。LiteralString
は、このような外部から取得した可能性がある危険な文字列と、安全な文字列定数を区別する場合に利用されます。
typing.reveal_type()¶
mypyなどで型チェックを行うとき、reveal_type()
を使うと変数の型をコンソールに出力できます。
$ cat test.py
a = 100
reveal_type(a)
$ mypy test.py
test.py:2: note: Revealed type is "builtins.int"
複雑なアノテーションを記述するときに便利な機能ですが、一つ問題がありました。reveal_type()
は mypy などの型チェッカでのみ実行可能な関数で、reveal_type()
が残ったPythonスクリプトを実行すると、エラーとなってしまいます。このため、開発中に型を確認しながらテストを実行する場合には reveal_type()
を入れたり消したりしなければならず、とても面倒でした。
Python 3.11では reveal_type()
が typing.reveal_type に追加され、こちらを使えば実行時にもエラーにならなくなりました。
$ cat test.py
import typing
a = 100
typing.reveal_type(a)
$ mypy test.py
test.py:2: note: Revealed type is "builtins.int"
$ python3.11 test.py
Runtime type is 'int'