型指定のベストプラクティス

はじめに

時間が経つにつれて、Python で型ヒントを使用する際に役立つベスト プラクティスが証明されています。 すべてのプラクティスがすべての状況に適用されるわけではなく、一部のプラクティスは個人的なスタイルや好みによるものですが、特定の理由がない限り、立ち戻るためのデフォルトの推奨事項として適しています。 逸脱する。

これらのベスト プラクティスは、特に型指定機能とエコシステムが成長するにつれて、絶えず進化しています。 したがって、型指定に関する経験についてお聞かせいただければ幸いです。 ディスカッションに参加する方法については、ディスカッションとサポート を参照してください。

型指定機能

型エイリアス

型エイリアスには TypeAlias を使用します (通常のエイリアスには使用しません)。

はい:

_IntList: TypeAlias = list[int]
g = os.stat
Path = pathlib.Path
ERROR = errno.EEXIST

いいえ:

_IntList = list[int]
g: TypeAlias = os.stat
Path: TypeAlias = pathlib.Path
ERROR: TypeAlias = errno.EEXIST

エルゴノミック プラクティス

Anyobject の使用

一般に、現在の型システムでは型を適切に表現できない場合、または正しい型を使用することが非人間的である場合は、Any を使用します。

たとえば、関数が引数として可能な限りすべてのオブジェクトを受け入れる場合 (たとえば、str() にのみ渡されるため)、型注釈として Any の代わりに object を使用します。 同様に、コールバックの戻り値が無視される場合は、次のように注釈を付けます object:

def call_cb_if_int(cb: Callable[[int], object], o: object) -> None:
    if isinstance(o, int):
        cb(o)

引数と戻り値の型

引数については、プロトコルと抽象型 (Mapping, Sequence, Iterable など) を優先します。 引数が文字通り任意の値を受け入れる場合は、Any の代わりに object を使用します。

戻り値については、具体的な実装には具体的な型 (list, dict など) を優先します。 プロトコルと抽象基本クラスの戻り値は、ケースバイケースで判断する必要があります。

はい:

def map_it(input: Iterable[str]) -> list[int]: ...
def create_map() -> dict[str, int]: ...
def to_string(o: object) -> str: ...  # 任意のオブジェクトを受け入れます

いいえ:

def map_it(input: list[str]) -> list[int]: ...
def create_map() -> MutableMapping[str, int]: ...
def to_string(o: Any) -> str: ...

場合によっては:

class MyProto(Protocol):
    def foo(self) -> list[int]: ...
    def bar(self) -> Mapping[str, str]: ...

isinstance() チェックが必要になるため、共用体の戻り値の型は避けてください。 必要に応じて Any または X | Any を使用します。

スタイリスティック プラクティス

省略形の構文

可能な場合は、共用体の省略形の構文を使用し、Union または Optional は使用しないでください。 None は共用体の最後の要素である必要があります。

はい:

def foo(x: str | int) -> None: ...
def bar(x: str | None) -> int | None: ...

いいえ:

def foo(x: Union[str, int]) -> None: ...
def bar(x: Optional[str]) -> Optional[int]: ...
def baz(x: None | str) -> None: ...

int | float の代わりに float を使用します。 Literal[None] の代わりに None を使用します。

組み込みジェネリクス

可能な場合は、typing のエイリアスの代わりに組み込みジェネリクスを使用します。

はい:

from collections.abc import Iterable

def foo(x: type[MyClass]) -> list[str]: ...
def bar(x: Iterable[str]) -> None: ...

いいえ:

from typing import Iterable, List, Type

def foo(x: Type[MyClass]) -> List[str]: ...
def bar(x: Iterable[str]) -> None: ...