2011-07-17 30 views
38

pythonは汎用/テンプレートタイプのシナリオをどのように扱いますか?外部ファイル "BinaryTree.py"を作成し、バイナリツリーを扱いたいとしますが、どのデータ型でも処理したいとします。Generics/templates in python?

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

+9

pythonにはアヒルのテンプレートがあります –

答えて

44

Pythonはduck typingを使用しているため、複数の型を扱うための特殊な構文は必要ありません。

C++のバックグラウンドをお持ちの方は、テンプレート関数/クラスで使用されている操作が(構文レベルの)タイプTで定義されている限り、そのタイプはTテンプレート内の

  1. はあなたがバイナリツリーに挿入するアイテムの種類の契約を定義します。

    だから、基本的に、それは同じように動作します。

  2. この契約(つまり、クラスのドキュメントで)
  3. が契約
  4. に指定された操作のみを使用してバイナリツリーを実装した文書は、あなたがない限り、明示的な型チェックを書くこと、しかし、注意しましょう

を楽しみます(通常は推奨されません)、バイナリツリーには選択したタイプの要素のみが含まれていることを強制することはできません。

+0

André、私はPythonで明示的な型チェックがなぜ通常は推奨されないのか理解したいと思います。動的に型指定された言語を使用しているように見えるので混乱しています。関数に入る可能性のある型を保証できない場合、問題が発生する可能性があります。しかし、やはり、私はPythonの新機能です。 :-) – ScottEdwards2000

+1

@ ScottEdwards2000 PEP 484でタイプヒントを使用した暗黙のタイプチェックと、タイプチェッカー –

+0

@ ScottEdwards2000を使用できます。タイプをチェックしないでください。なぜなら、Pythonはダックタイプなのでです。誰かがあなたの機能を別のクラスで使いたいと思っているのなら、それはあなた次第です。あなたのカモのように振る舞うものを渡す限り、実際にはアヒルか偽のアヒルかは気にしないでください。 – McKay

0

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

2

Pythonは動的に型指定されるため、多くの場合、オブジェクトの型は問題になりません。何かを受け入れることは良い考えです。

私が何を意味するか示すために、この木のクラスはその2本の枝のために何かを受け入れる:

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

そして、それはこのように使用することができます:pythonが動的型付けされているので

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

オブジェクトの種類は重要です。コンテナの項目をループして、各オブジェクトでメソッド 'foo'を呼び出す場合は、文字列をコンテナに入れるのは悪い考えです。 *何でも受け入れるのは良い考えではありません。ただし、コンテナ内のすべてのオブジェクトがクラス「HasAFooMethod」から派生する必要はありません。 –

+1

実際に、タイプは*問題ありません:注文しなければなりません。 –

+0

ああ、OK。私はその時誤解した。 – Andrea

3

、この超簡単です。実際には、BinaryTreeクラスがデータ型を扱わないように余分な作業をする必要があります。

たとえば、key()のようなメソッドからオブジェクト内のオブジェクトをツリー内に配置するために使用されるキー値を指定する場合は、オブジェクトのkey()を呼び出すだけです。あなたはクラスobject_to_insertの種類が何であるかを定義する必要はありません

class BinaryTree(object): 

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

注:たとえば。 key()メソッドを持っている限り、それは動作します。

例外は、文字列や整数などの基本データ型を使用する場合です。あなたはそれらを包括的なBinaryTreeで動作させるためにそれらをクラスにラップする必要があります。それがあまりにも重いと思って、あなたが実際に文字列を格納するだけの余分な効率を望むなら、それはPythonがうまくいきません。

+3

それとは逆に、すべてのデータ型はPythonのオブジェクトです。彼らは(Integer' boxing/unboxingでJavaのように)ラップする必要はありません。 – thirtythreeforty

1

幸いにも、Pythonでの汎用プログラミングのためのいくつかの取り組みがあります。 ライブラリがあります。ここではgeneric

はそれのためのドキュメントです:http://generic.readthedocs.org/en/latest/

それは長年にわたって進行していないが、あなたはどのように独自のライブラリを作る&を使用する大まかなアイデアを持つことができます。

乾杯

12

実は今、あなたは、Python 3.5+でジェネリックを使用することができます。 PEP-484およびtyping library documentationを参照してください。

私のプラクティスによれば、Java Genericsに精通している人にとっては、特にシームレスかつ明確ではありません。

2

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.__name__)) 

        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つの別々のクラスから継承します。これを克服するには、新しいクラスを作成するのではなく、内部クラスのさまざまなフォームを格納し、前に作成した内部クラスを返すように辞書を作成する必要があります。これにより、同じパラメータを持つ重複するタイプが作成されるのを防ぐことができます。興味があれば、デコレータやメタクラスでより洗練されたソリューションを作ることができます。

+0

上記の例で辞書をどのように使用できるかについて詳しく説明できますか?あなたはgitか何かのどちらかのためのスニペットを持っていますか?ありがとうございました.. – samiunn

+0

私には例がありませんし、今はちょっと時間がかかるかもしれません。しかし、原則はそれほど難しいことではありません。 dictはキャッシュとして機能します。新しいクラスが作成時には、型パラメーターを調べて、その型およびパラメーター構成のIDを作成する必要があります。それから、それを以前の既存のクラスを検索するためのキーとして使用することができます。この方法で、それはそのクラスを繰り返し使用します。 –