web-dev-qa-db-ja.com

Python 3.6で実行時に変数をUnionタイプに対してチェックします

Python 3.6型ヒントを使用して関数のデコレータを記述しようとしています。引数のディクショナリが型ヒントを尊重していることを確認し、問題の明確な説明でエラーが発生しない場合は、 HTTP APIに使用できます。

問題は、関数にUnionタイプを使用するパラメーターがある場合、実行時に変数に対して変数をチェックできないことです。

たとえば、私はこの機能を持っています

_from typing import Union
def bark(myname: str, descr: Union[int, str], mynum: int = 3) -> str:
    return descr + myname * mynum
_

できます:

_isinstance('Arnold', bark.__annotations__['myname'])
_

だがしかし:

_isinstance(3, bark.__annotations__['descr'])
_

Unionisinstanceまたはissubclassと一緒に使用できないためです。

タイプオブジェクトを使用してそれをチェックする方法が見つかりませんでした。自分でチェックを実装しようとしましたが、_bark.__annotations__['descr']_が_typing.Union[int, str]_としてREPL=に表示されている間は、実行時に型のリストにアクセスできませんbark.__annotations__['descr'].__repr__()を調べるという醜いハックを使用します。

この情報にアクセスする適切な方法はありますか?それとも、実行時に簡単にアクセスできないように意図的に意図されていますか?

13
Jacopofar

__args__UnionTupleを保持する "可能なコンテンツ:

>>> from typing import Union

>>> x = Union[int, str]
>>> x.__args__
(int, str)
>>> isinstance(3, x.__args__)
True
>>> isinstance('a', x.__args__)
True

__args__引数はドキュメント化されていないため、「実装の詳細をいじる」と考えることができますが、reprを解析するよりも良い方法のようです。

4
MSeifert

MSeifert( https://stackoverflow.com/a/45959000/743342 )による既存の受け入れられた回答は、Unionsを他のジェネリック型と区別せず、実行時に判別することは困難です型注釈がUnionであるか、Mappingのような他の一般的な型であるかは、パラメータ化されたUnionでのisinstance()およびissubclass()の動作によるタイプ。

ジェネリック型には、それを作成するために使用された元のジェネリック型への参照が含まれる、文書化されていない__Origin__属性があるようです。タイプアノテーションがパラメータ化されたUnionであることを確認したら、ドキュメント化されていない__args__属性を使用してタイプパラメータを取得できます。

>>> from typing import Union
>>> type_anno = Union[int, str]
>>> type_anno.__Origin__ is Union
True
>>> isinstance(3, type_anno.__args__)
True
>>> isinstance('a', type_anno.__args__)
True
7
Richard Xia

typeguardでインストールできるpipモジュールを使用できます。関数check_argument_typesまたは関数デコレータ@typecheckedを提供します。ランタイムタイプチェックを実行する必要があります: https://github.com/agronholm/typeguard

from typing import Union
from typeguard import check_argument_types, typechecked

def check_and_do_stuff(a: Union[str, int]) -> None:
    check_argument_types() 
    # do stuff ...

@typechecked
def check_decorator(a: Union[str, int]) -> None:
    # do stuff ...

check_and_do_stuff("hello")
check_and_do_stuff(42)
check_and_do_stuff(3.14)  # raises TypeError

別の理由で単一の変数の型をチェックする場合は、typeguardのcheck_type関数を直接使用できます。

from typing import Union
from typeguard import check_type

MyType = Union[str, int]

check_type("arg", "string", MyType, None)  # OK
check_type("arg", 42, MyType, None)  # OK
check_type("arg", 3.5, MyType, None)  # raises TypeError

この例では、"arg"およびNone引数は使用されていません。 check_type関数はこのモジュールのパブリック関数として文書化されていないため、そのAPIは変更される可能性があることに注意してください。

6
Jindra Helcl