2017-09-23 8 views
1

私はタイプヒンティングで使用するためにPythonで自分のパラメータ化された型を作成したい:Pythonで独自の "パラメータ化された"型を作成するにはどうすればいいですか( `Optional [T]`など)?

class MaybeWrapped: 
    # magic goes here 

T = TypeVar('T') 

assert MaybeWrapped[T] == Union[T, Tuple[T]] 

不自然な例を気にしません。これをどのように実装できますか?私はUnionとOptionalのソースを見ていましたが、私が避けたいと思っているかなり低レベルのハッカーのように見えます。

ドキュメントの唯一の提案は、example re-implementation of Mapping[KT,VT] that inherits from Genericからのものです。しかし、その例は、クラスそのものよりも__getitem__メソッドのほうがはっきりしています。

答えて

1

をジェネリックなクラスや関数を作成しようとしているだけの場合は、documentation on mypy-lang.org about generic typesを見てみてください。これはかなり包括的で、標準ライブラリ型のドキュメントより詳細です。

あなたの具体的な例を実装しようとしている場合、それは指摘する価値があることtype aliases work with typevars - あなたは簡単に行うことができます。

from typing import Union, TypeVar, Tuple 

T = TypeVar('T') 

MaybeWrapped = Union[T, Tuple[T]] 

def foo(x: int) -> MaybeWrapped[str]: 
    if x % 2 == 0: 
     return "hi" 
    else: 
     return ("bye",) 

# When running mypy, the output of this line is: 
# test.py:13: error: Revealed type is 'Union[builtins.str, Tuple[builtins.str]]' 
reveal_type(foo(3)) 

しかし、あなたは本当に新しいとジェネリック型を構築しようとしている場合セマンティクスは、運が悪い可能性が非常に高いです。

  1. PEP 484準拠の型チェッカーがはそれをを理解して使用することができますカスタムクラス/メタクラスの事のいくつかの種類を構築する:あなたの残りのオプションがします。
  2. 何らかの形で使用しているタイプチェッカーを変更してください(例えば、mypyには実験的な "プラグイン"システムがあります)
  3. 新しいカスタムタイプを組み込むようにPEP 484を修正してもいいですか? typing module repo)。
+0

ありがとう、ここで 'TypeVar'を使用して私が逃したものです。ジェネリックスがこのように「ちょうどうまくいく」というのはかなり驚きです。 – shadowtalker

2

正確には、すべての魔法を実行する方法は__getitem__です。

これは、[]の角かっこで1つの名前を登録するときに呼び出されるメソッドです。

したがって、クラスのクラスには__getitem__メソッドが必要です。つまり、そのメタクラスは、角括弧内のパラメータを取得します。このメソッドは、生成したいものを動的に作成(またはキャッシュされたコピーを取得)し、それを返す責任があります。

タイピングライブラリがすべての妥当なケースをカバーしているように見えるので、これをタイプヒントとしてどのようにしたいのか想像できません(私はすでにカバーしていない例は考えられません)。しかし、あなたはクラスが自身のコピーを返すようにしたいと仮定しましょう、しかしそのtype_属性としてanotatedパラメータを持つ:

class MyMeta(type): 
    def __getitem__(cls, key): 
     new_cls = types.new_class(f"{cls.__name__}_{key.__name__}", (cls,), {}, lambda ns: ns.__setitem__("type", key)) 
     return new_cls 

class Base(metaclass=MyMeta): pass 

そして、対話モードでこれをしようとする上で、1が行うことができます:

In [27]: Base[int] 
Out[27]: types.Base_int 
+0

ありがとう、私はまだこれがどのように動作するのかよく分かりませんが。あなたは私の質問の例に話すことができますか?あなたの答えの例は私がやろうとしているものと少し違っているようです。 – shadowtalker

+1

@jsbueno - あなたの答えはうまくいかないと思います。あなたが提案したコードは、* PEP 484のようなものを作る方法ですが、そうでないので、PEP 484に準拠した型チェッカーはあなたの 'MyMeta'や' Base'クラス。 – Michael0x2a