web-dev-qa-db-ja.com

Pythonのジェネリック/テンプレート?

pythonジェネリック/テンプレートタイプのシナリオをどのように処理しますか?外部ファイル "BinaryTree.py"を作成し、バイナリデータをすべてのデータタイプに対応させたいとします。

したがって、カスタムオブジェクトの型を渡し、そのオブジェクトのバイナリツリーを作成できます。これはPythonでどのように行われますか?

60
keys

Pythonは ダックタイピング を使用するため、複数のタイプを処理するための特別な構文は必要ありません。

C++のバックグラウンドを使用している場合、テンプレート関数/クラスで使用される操作がT(構文レベル)の一部の型で定義されている限り、その型を使用できることを覚えているでしょう。テンプレートのT

したがって、基本的には同じように機能します。

  1. バイナリツリーに挿入するアイテムのタイプのコントラクトを定義します。
  2. この契約を文書化します(つまり、クラス文書に)
  3. コントラクトで指定された操作のみを使用してバイナリツリーを実装する
  4. 楽しい

ただし、明示的な型チェック(通常は推奨されません)を記述しない限り、選択した型の要素のみがバイナリツリーに含まれることを強制することはできません。

59
André Caron

実際には、Python 3.5+。でジェネリックを使用できます。 PEP-484 および typing library documentation を参照してください。

私の慣習によれば、特にJava Genericsに精通しているが、まだ使用可能な人にとっては、それほどシームレスで明確ではありません。

16
8bitjoey

Pythonでジェネリック型を作成することについていくつかの良い考えを思いついた後、同じアイデアを持っている他の人を探し始めましたが、何も見つかりませんでした。だから、ここにある。これを試してみましたが、うまくいきます。これにより、Pythonで型をパラメーター化できます。

_class List( type ):

        def __new__( type_ref, member_type ):

            class List( list ):

                def append( self, member ):

                    if not isinstance( member, member_type ):
                        raise TypeError( 'Attempted to append a "{0}" to a "{1}" which only takes a "{2}"'.format(
                            type( member ).__name__,
                            type( self ).__name__,
                            member_type.__) )

                    list.append( self, member )

            return List 
_

これで、このジェネリック型から型を派生できます。

_class TestMember:
        pass

class TestList( List( TestMember ) ):

    def __init__( self ):
        super().__init__()


test_list = TestList()
test_list.append( TestMember() )
test_list.append( 'test' ) # This line will raise an exception
_

このソリューションは単純化されており、制限があります。ジェネリック型を作成するたびに、新しい型が作成されます。したがって、List( str )を親として継承する複数のクラスは、2つの別個のクラスから継承します。これを克服するには、新しいクラスを作成するのではなく、内部クラスのさまざまな形式を格納し、以前に作成した内部クラスを返すための辞書を作成する必要があります。これにより、同じパラメーターを持つ重複タイプが作成されなくなります。興味があれば、デコレータやメタクラスを使用してよりエレガントなソリューションを作成できます。

8

pythonは動的に型指定されるため、これは非常に簡単です。実際、BinaryTreeクラスがデータ型と連動しないようにするには、余分な作業を行う必要があります。

たとえば、key()などのメソッドからオブジェクト内のオブジェクトをツリーに配置するために使用されるキー値が必要な場合は、オブジェクトでkey()を呼び出すだけです。例えば:

_class BinaryTree(object):

    def insert(self, object_to_insert):
        key = object_to_insert.key()
_

Object_to_insertの種類を定義する必要は決してないことに注意してください。 key()メソッドがあれば機能します。

例外は、文字列や整数などの基本的なデータ型で動作させたい場合です。それらをクラスにラップして、ジェネリックBinaryTreeで動作させる必要があります。それが重すぎるように聞こえ、実際に文字列を保存するだけの余分な効率が必要な場合は、申し訳ありませんが、それはPythonが得意ではありません。

3
Leopd

Pythonは動的に型指定されるため、オブジェクトの型は多くの場合重要ではありません。何でも受け入れることをお勧めします。

私が意味することを示すために、このツリークラスは2つのブランチに対して何でも受け入れます。

class BinaryTree:
    def __init__(self, left, right):
        self.left, self.right = left, right

そして、次のように使用できます。

branch1 = BinaryTree(1,2)
myitem = MyClass()
branch2 = BinaryTree(myitem, None)
tree = BinaryTree(branch1, branch2)
2
Andrea

組み込みのコンテナがどのようにそれを行うかを見てください。 dictおよびlistなどには、任意のタイプの異種要素が含まれます。たとえば、ツリーのinsert(val)関数を定義すると、ある時点でnode.value = valとPythonが残りを処理します。

1
John Zwinck

幸いなことにpython。の一般的なプログラミングのためのいくつかの努力がありました。ライブラリがあります: generic

以下にそのドキュメントを示します。 http://generic.readthedocs.org/en/latest/

それは何年にもわたって進歩していませんが、あなたはあなた自身のライブラリをどのように使い、作るかについて大まかな考えを持つことができます。

乾杯

1
igaurav

Python 2を使用する場合、またはJavaコードを書き直したい場合。これは実際の解決策ではありません。 https://github.com/FlorianSteenbuck/python-generics 私はまだコンパイラを入手していないので、あなたは現在そのように使用しています:

class A(GenericObject):
    def __init__(self, *args, **kwargs):
        GenericObject.__init__(self, [
            ['b',extends,int],
            ['a',extends,str],
            [0,extends,bool],
            ['T',extends,float]
        ], *args, **kwargs)

    def _init(self, c, a, b):
        print "success c="+str(c)+" a="+str(a)+" b="+str(b)

TODO

  • コンパイラ
  • ジェネリッククラスとジェネリック型を使用する(<? extends List<Number>>
  • superサポートを追加
  • 追加 ? サポート
  • コードのクリーンアップ
1