アノテーションにおける特別な型¶
Any¶
Any は未知の静的型を表します。
すべての型は Any に assignable であり、Any はすべての型に割り当て可能です。
Any についての詳細な議論は 型システムの概念 を参照してください。
アノテーションのない関数パラメータは Any でアノテートされていると見なされます。 ジェネリック型が型パラメータを指定せずに使用される場合、それらは Any と見なされます:
from collections.abc import Mapping
def use_map(m: Mapping) -> None: # Mapping[Any, Any] と同じ
...
このルールは tuple にも適用され、アノテーションコンテキストでは tuple[Any, ...] と同等です。 同様に、アノテーション内の裸の Callable は Callable[..., Any] と同等です:
from collections.abc import Callable
def check_args(args: tuple) -> bool:
...
check_args(()) # OK
check_args((42, 'abc')) # これも OK
check_args(3.14) # 型チェッカーによってエラーとしてフラグが立てられる
# 任意のコール可能オブジェクトのリストはこの関数によって受け入れられます
def apply_callbacks(cbs: list[Callable]) -> None:
...
Any は基底クラスとしても使用できます。 これは、どこでもダックタイプできるクラスや非常に動的なクラスで型チェッカーのエラーを回避するのに役立ちます。
None¶
型ヒントで使用される場合、式 None は type(None) と同等と見なされます。
NoReturn¶
typing モジュールは、通常は決して戻らない関数をアノテートするための special form NoReturn を提供します。 たとえば、無条件に例外を発生させる関数:
from typing import NoReturn
def stop() -> NoReturn:
raise RuntimeError('no way')
NoReturn アノテーションは sys.exit などの関数に使用されます。 静的型チェッカーは、NoReturn を返すとアノテートされた関数が本当に戻らないことを確認します。暗黙的または明示的に:
import sys
from typing import NoReturn
def f(x: int) -> NoReturn: # エラー、f(0) は暗黙的に None を返す
if x != 0:
sys.exit(1)
チェッカーは、そのような関数への呼び出し後のコードが到達不能であることを認識し、それに応じて動作します:
# 最初の例から続ける
def g(x: int) -> int:
if x > 0:
return x
stop()
return 'whatever works' # 一部のチェッカーは到達不能なブロック内のエラーを無視するため、エラーが報告されない場合があります
Never¶
Python 3.11 以降、typing モジュールには special form Never が含まれています。 これは、空の Python オブジェクトのセットを表すボトム型を表します。
Never 型は上記の NoReturn と同等です。 NoReturn 型は関数の戻り値のアノテーションに慣習的に使用され、Never は他の場所で使用されることが一般的ですが、2 つの型は完全に交換可能です。
float および complex の特別なケース¶
Python の数値型 complex、float および int は互いのサブタイプではありませんが、一般的なユースケースをサポートするために、型システムには簡単なショートカットが含まれています。
引数が float 型としてアノテートされている場合、int 型の引数は許容されます。同様に、引数が complex 型としてアノテートされている場合、float または int 型の引数は許容されます。
type[]¶
クラスオブジェクト、特に特定のクラスを継承するクラスオブジェクトについて話したい場合があります。 これは type[C] として表記できます。 C はクラスです。 明確にするために: C (アノテーションで使用される場合) はクラス C のインスタンスを指しますが、type[C] は C の サブクラス を指します。 (これは object と type の間の区別と似ています。)
たとえば、次のクラスがあるとします:
class User: ... # User クラスの抽象基底
class BasicUser(User): ...
class ProUser(User): ...
class TeamUser(User): ...
そして、クラスオブジェクトを渡すとこれらのクラスのインスタンスを作成する関数があるとします:
def new_user(user_class):
user = user_class()
# (ここでユーザーオブジェクトをデータベースに書き込むことができます)
return user
type[] をサブスクリプトしない場合、new_user() をアノテートするためにできる最善の方法は次のとおりです:
def new_user(user_class: type) -> User:
...
ただし、type[] と上限を持つ型変数を使用すると、はるかに優れた方法が可能です:
U = TypeVar('U', bound=User)
def new_user(user_class: type[U]) -> U:
...
これで、特定の User サブクラスを使用して new_user() を呼び出すと、型チェッカーは結果の正しい型を推測します:
joe = new_user(BasicUser) # 推測された型は BasicUser です
type[C] に対応する値は、C のサブタイプである実際のクラスオブジェクトでなければなりません。 つまり、上記の例では、new_user(BasicUser | ProUser) を呼び出すと、型チェッカーによって拒否されます (ランタイムで失敗することに加えて、結合をインスタンス化できないため)。
type[] のパラメータとしてクラスの結合を使用することは合法です。次のように:
def new_non_team_user(user_class: type[BasicUser | ProUser]):
user = new_user(user_class)
...
type[] は結合に分配されます:
type[A | B] は type[A] | type[B] と equivalent です。
ただし、ランタイムで渡される実際の引数は具体的なクラスオブジェクトでなければなりません。上記の例では:
new_non_team_user(ProUser) # OK
new_non_team_user(TeamUser) # 型チェッカーによって許可されていません
type[Any] もサポートされています (その意味については以下を参照してください)。
T が型変数である場合、クラスメソッドの最初の引数をアノテートする場合に type[T] が許可されます (関連セクションを参照してください)。
tuple や Callable などの他の特別な構成要素は、type の引数として許可されていません。
この機能にはいくつかの懸念があります。たとえば、new_user() が user_class() を呼び出すと、User のすべてのサブクラスがそのコンストラクタシグネチャでこれをサポートする必要があることを意味します。 ただし、これは type[] に固有のものではありません。クラスメソッドにも同様の懸念があります。 型チェッカーはそのような仮定の違反をフラグ付けする必要がありますが、デフォルトでは、指定された基底クラス (上記の例では User) のコンストラクタシグネチャに一致するコンストラクタ呼び出しが許可されるべきです。 複雑または拡張可能なクラス階層を含むプログラムは、ファクトリクラスメソッドを使用してこれを処理することもできます。
type がパラメータ化される場合、正確に 1 つのパラメータが必要です。 かっこなしのプレーンな type は、Python のメタクラス階層のルートであり、type[Any] と同等です。
type[Any] (または type) の動作に関しては、この型の変数の属性にアクセスすると、type によって定義された属性とメソッド (たとえば、__repr__() および __mro__) のみが提供されます。 そのような変数は任意の引数で呼び出すことができ、戻り値の型は Any です。
type はそのパラメータに対して共変です。なぜなら、type[Derived] は type[Base] のサブタイプだからです:
def new_pro_user(pro_user_class: type[ProUser]):
user = new_user(pro_user_class) # OK
...