web-dev-qa-db-ja.com

Optional型ヒントを使用するにはどうすればよいですか?

Optionalタイプヒントの使用方法を理解しようとしています。 PEP 434から、def test(a: int = None)またはdef test(a: Union[int, None])としてdef test(a: Optional[int])Optionalを使用できることがわかりました。

しかし、次の例はどうですか?

def test(a : dict = None):
    #print(a) ==> {'a': 1234}
    #or
    #print(a) ==> None

def test(a : list = None):
    #print(a) ==> [1,2,3,4, 'a', 'b']
    #or
    #print(a) ==> None

Optional[type]Union[type, None]と同じことを意味するようですが、なぜOptional[]を使用する必要があるのですか?

15
jacobcan118

Optional[...]Union[..., None]の略記法であり、特定のタイプのオブジェクトが必要であることをタイプチェッカーに伝える、またはNoneが必要です。 ...は、複雑な複合型またはUnion[]を含む有効な型のヒントを表します。デフォルト値Noneを持つキーワード引数がある場合は常に、Optionalを使用する必要があります。

したがって、2つの例では、dictおよびlistコンテナタイプがありますが、aキーワード引数のデフォルト値は、Noneも許可されていることを示しています。 Optional[...]を使用:

from typing import Optional

def test(a: Optional[dict] = None) -> None:
    #print(a) ==> {'a': 1234}
    #or
    #print(a) ==> None

def test(a: Optional[list] = None) -> None:
    #print(a) ==> [1, 2, 3, 4, 'a', 'b']
    #or
    #print(a) ==> None

Optional[]Union[]を使用することと、Union[]に単にNoneを追加することとの間に技術的な違いはないことに注意してください。したがって、Optional[Union[str, int]]Union[str, int, None]はまったく同じものです。

個人的には、デフォルト値を設定するためにOptional[]を使用するキーワード引数のタイプを設定するときに= Noneを使用してalwaysに固執します。これはNoneの方が適切です。さらに、Union[...]部分を別の型エイリアスに移動したり、引数が必須になった場合に後でOptional[...]部分を削除したりするのが簡単になります。

たとえば、あなたが持っていると言います

from typing import Optional, Union

def api_function(optional_argument: Optional[Union[str, int]] = None) -> None:
    """Frob the fooznar.

    If optional_argument is given, it must be an id of the fooznar subwidget
    to filter on. The id should be a string, or for backwards compatibility,
    an integer is also accepted.

    """

Union[str, int]を型エイリアスに引き出すことでドキュメントが改善されます:

from typing import Optional, Union

# subwidget ids used to be integers, now they are strings. Support both.
SubWidgetId = Union[str, int]


def api_function(optional_argument: Optional[SubWidgetId] = None) -> None:
    """Frob the fooznar.

    If optional_argument is given, it must be an id of the fooznar subwidget
    to filter on. The id should be a string, or for backwards compatibility,
    an integer is also accepted.

    """

Union[]の代わりにOptional[...]が使用されたため、Union[str, int, None]をエイリアスに移動するリファクタリングが非常に簡単になりました。結局、None値は「サブウィジェットID」ではなく、値の一部ではありません。Noneは値がないことを示すためのものです。

サイドノート:ただし、どのタイプを含める必要があるかについては何も言えないので、タイプヒントで標準ライブラリコンテナータイプを使用しないでください。そのため、dictlistの代わりに、typing.Listtyping.Dictを使用します。そして、コンテナタイプからreadingのみの場合、不変の抽象コンテナタイプを受け入れることもできます。リストとタプルはSequenceオブジェクトですが、dictMappingタイプです:

from typing import Mapping, Optional, Sequence, Union

def test(a: Optional[Mapping[str, int]] = None) -> None:
    """accepts an optional map with string keys and integer values"""
    # print(a) ==> {'a': 1234}
    # or
    # print(a) ==> None

def test(a: Optional[Sequence[Union[int, str]]] = None) -> None:
    """accepts an optional sequence of integers and strings
    # print(a) ==> [1, 2, 3, 4, 'a', 'b']
    # or
    # print(a) ==> None
22
Martijn Pieters