2017-06-24 18 views
4

私はPython 3.6.1、mypy、およびタイピングモジュールを使用しています。私は2つのカスタムタイプ、FooBarを作成してから、それらを関数から返されたdictで使用しました。この辞書は、strからUnionへのマッピングとして、FooBarと記載されています。Typing.UnionをPythonのサブタイプの1つにキャストする方法は?

process(d["foo"], d["bar"]) 
# typing-union.py:15: error: Argument 1 to "process" has incompatible type "Union[Foo, Bar]"; expected "Foo" 
# typing-union.py:15: error: Argument 2 to "process" has incompatible type "Union[Foo, Bar]"; expected "Bar" 

や型を使用して::

-であるように私は、値を使用してみました

from typing import Dict, Union, NewType 

Foo = NewType("Foo", str) 
Bar = NewType("Bar", int) 

def get_data() -> Dict[str, Union[Foo, Bar]]: 
    return {"foo": Foo("one"), "bar": Bar(2)} 

def process(foo_value: Foo, bar_value: Bar) -> None: 
    pass 

d = get_data() 

:それから私は名前だけ1引数それぞれの機能で、この辞書からの値を使用したいです

process(Foo(d["foo"]), Bar(d["bar"])) 
# typing-union.py:20: error: Argument 1 to "Foo" has incompatible type "Union[Foo, Bar]"; expected "str" 
# typing-union.py:20: error: Argument 1 to "Bar" has incompatible type "Union[Foo, Bar]"; expected "int" 

Unionをそのサブタイプの1つにキャストするにはどうすればよいですか?

答えて

3

あなたはcast()を使用する必要があるだろう:

process(cast(Foo, d["foo"]), cast(Bar, d["bar"])) 

PEP 484のキャストセクションから:プログラマは可能性があります

時折型チェッカーは、ヒントの異なる種類を必要とするかもしれません式がタイプチェッカよりも拘束タイプのものであることがわかる。

特定のタイプの値が辞書のキーの特定の値にどのようなスペルがあるかを特定する方法はありません。あなたは、キーごとに入力することができ、代わりにnamed tupleを返すことを検討する必要があります。

from typing import Dict, Union, NewType, NamedTuple 

Foo = NewType("Foo", str) 
Bar = NewType("Bar", int) 

class FooBarData(NamedTuple): 
    foo: Foo 
    bar: Bar 

def get_data() -> FooBarData: 
    return FooBarData(foo=Foo("one"), bar=Bar(2)) 

さて型hinterが知っている正確各属性タイプが何であるか:

d = get_data() 
process(d.foo, d.bar) 
+0

ありがとうございます! (a)実際のコードではdictが可変長であるため、(b)私はnamedtupleセマンティクスのファンではありません(例えば、ドットアクセスとアイテムベースのアクセスを混在させる)。 –

+0

@ChrisWarrick:もしあなたが異種の可変長データ構造を持っていれば、名前付きタプルはそれをカットしません。 –

1

私はキャストがあると思いますが、あなたのケースではおそらく適切なオプションを使用するために、同様のシナリオに適用可能な追加のオプションを簡単に言及したいと思います。

実際にはあなたのdictは最新のバージョンのmypy(githubリポジトリからクローンした場合)で利用可能な新しい実験的なTypedDict機能をより正確に使用し、次のpypiリリースで利用可能になるでしょう。

TypedDictを使用するには、pip install mypy_extensionsを実行してpypiからmypy_extensionsをインストールする必要があります。

from mypy_extensions import TypedDict 

Foo = NewType("Foo", str) 
Bar = NewType("Bar", int) 

FooBarData = TypedDict('FooBarData', { 
    'foo': Foo, 
    'bar': Bar, 
}) 

あなたはまた、Pythonでクラスベースの構文を使用してFooBarDataを定義することができ3.6+:あなたはまた、言及

from mypy_extensions import TypedDict 

Foo = NewType("Foo", str) 
Bar = NewType("Bar", int) 

class FooBarData(TypedDict): 
    foo: Foo 
    bar: Bar 

TypedDictはあなたのdictの各項目に個々のタイプを割り当てることができますあなたの辞書は動的な要素を持つことができます。それが真にダイナミックなら、TypedDictはNamedTupleが助けにならないのと同じ理由では役に立ちませんが、TypedDictが最終的に有限個の要素を持ち、すべてではなくアイテムを徐々に追加している一度にnon-total TypedDictsを試してみるか、mix required and non-required itemsというTypeDictsを構築してみてください。

他のほとんどすべてのタイプとは異なり、TypedDictsは公称タイピングではなく構造タイピングを使用してチェックされます。これは、同じタイプのFooBarDataフィールドのfooフィールドとbarフィールドも持つ完全に無関係なTypedDict(QuxData)を定義した場合、実際にFooBarDataという有効なサブタイプになります。これはちょっとした巧みさでいくつかの興味深い可能性を開くかもしれません。

関連する問題