2017-03-03 11 views
1

mypyドキュメントによれば、クラスが自身を参照する必要がある場合は、forward-referenceを使用できます。mypyのNamedTupleから継承したクラスのメソッドの型チェックを実装するにはどうすればよいですか?

これは通常のクラスではうまくいくようですが、NamedTupleから継承したクラスではうまく動作しません。

""" 
All this code runs without error on Python 3.6 

The question is why the 'B' class' __add__ method 
raises an error through mypy. 
""" 


from typing import * 

class A: 
    def __init__(self, x: int) -> None: 
     self.x = x 

    def __add__(self, other: 'A') -> 'A': 
     return type(self)(self.x + other.x) 

    def __str__(self) -> str: 
     return f'A(x={self.x})' 

A1 = A(1) 
A2 = A(2) 
A3 = A1 + A2 
print(A3) 

class B(NamedTuple('B', [('x', int)])): 

    # The following line will raise an error in mypy 
    # error: Argument 1 of "__add__" incompatible with supertype "tuple" 
    def __add__(self, other: 'B') -> 'B': 
     return type(self)(self.x + other.x) 

B1 = B(1) 
B2 = B(2) 
B3 = B1 + B2 
print(B3) 

更新:Guido van Rossum自身が以後回答していますthis question on Github

あなたが達成しようとしていることを100%確信しているわけではありませんが、最初の例に基づいて、クラスBに対して+を再定義して、 B. mypyがデフォルトでこれをサポートしていない理由は、「Liskov置換原則」と呼ばれるものです(説明のためGoogleにそれを伝えることができます)。

回避策があります:put#type:エラーを生成する行を無視します(def には行を追加します)。これは健全ではありませんが、Bインスタンスをタプルとみなしてタプル連結を試みるコードに決して渡すことができない限り、必要な処理を行います。

+0

これは奇妙です。 'class B'は既に' __name__' 'B'を持つクラスから継承していることに注意してください...多分それは物事を混乱させるでしょう。だから '__mro__'は'( __main __。B '>、) ' –

+0

のようになります。 'B'の名前、または' namedtuple'に渡す名前です。クラスB(NamedTuple( 'SuperB'、[( 'x'、int)]))): ' –

+0

これは関連するかもしれません:https://github.com/python/mypy/issues/1237 –

答えて

1

B.__add__ための型宣言は、それがBの別のインスタンスにBのインスタンスを追加するためにのみ有効です(と、それは仕事にother.xを期待するので実装は、それをバックアップする)ことを示唆しています。しかし、その方法は、tuplenamedtuple経由)よりも一般的な__add__メソッドをオーバーライドします。これにより、2つのタプルを連結することができます(右辺はtupleまたはtuple -subclassのインスタンスになる可能性があります)。あなたの新しいメソッドはその引き数に厳しい型の要件を持っているので、クラスはtupleの適切なサブクラスではありません(mypyの視点から)。クラスのインスタンスを、以前にタプルが使用されていた場所にドロップしたり、同じ方法で動作させることはできません。

は、この関数を考えてみましょう:

def tuple_append(tup: tuple, value: any) -> tuple: 
    return tup1 + (value,) 

は、これは通常のタプルのために働くだろうが、あなたがtupとしてあなたBインスタンスのいずれかを渡した場合、それはあなたがtupleサブクラスのインスタンスに渡しているにもかかわらず、(失敗します関数の型宣言が必要とするため)。このため、mypyは、Bタイプが有効であるとはみなされません。 Bを受け入れると、正しい型宣言を持つ他のコードが突然破損する可能性があります。

残念ながら、私はこの問題の周りに良い方法はないと思います。 Pythonには、他の言語が行う「私的継承」という概念はありません。他のクラスから実装を継承する方法はありません。これは、公にサブクラスになることもありません。

関連する問題