2017-10-11 76 views
1
適當類型提示

我試圖實現一種自定義的序列類在Python:子類序列可以用Python

from typing import Sequence, TypeVar, List 

T = TypeVar('T') 

class MySequence(Sequence[T]): 
    def __init__(self): 
     self._container: Sequence[T] = [] 
    def __getitem__(self, idx): 
     return self._container[idx] 
    def __len__(self): 
     return len(self._container) 

現在我要檢查mypy意識到的MySequence元素項目T類型:

foo: MySequence[str] = MySequence() 
reveal_type(foo[0]) 
# Revealed type is 'Any' 

所以失敗:mypy一無所知的foo項目。對於普通Sequence同樣的例子作品:

bar: Sequence[str] = [] 
reveal_type(bar[0]) 
# Revealed type is 'builtins.str*' 

如果我想類型添加註解__getitem__實現,我有另外一個錯誤:

def __getitem__(self, idx) -> T: 
# Signature of "__getitem__" incompatible with supertype "Sequence" 

我也試過

def __getitem__(self, idx) -> Union[T, Sequence[T]]: 

因爲idx可以是一個切片,在這種情況下,我的代碼將返回一個序列而不是一個元素。它使用相同的消息失敗。

正如my previous question討論,對這樣的問題進行open discussion

但是,我仍然想知道,是否可以創建自定義序列類型,允許mypy提取有關其項目類型的信息,如我的示例中所示?

回答

2

在這種情況下,做正確的事情是正確覆蓋exact signature for __getitem__,包括過載。

from typing import Sequence, TypeVar, List, overload, Union 

T = TypeVar('T', covariant=True) 

class MySequence(Sequence[T]): 
    def __init__(self): 
     self._container: Sequence[T] = [] 

    @overload 
    def __getitem__(self, idx: int) -> T: ... 

    @overload 
    def __getitem__(self, s: slice) -> Sequence[T]: ... 

    def __getitem__(self, item): 
     if isinstance(item, slice): 
      raise Exception("Subclass disallows slicing") 

     return self._container[item] 

    def __len__(self) -> int: 
     return len(self._container) 

foo: MySequence[str] = MySequence() 
reveal_type(foo[0]) 

(請注意,我做了typevar協變的。這不,嚴格的講,必需的,但如果容器實際上意味着以代表「只讀」樣的結構,我們不妨最大的靈活性)


注:即mypy決定返回類型是任何在第一個例子是預期的行爲的事實。根據PEP 484,而不類型的註釋的任何方法或簽名視爲好像該參數和返回類型都是Any

這是設計成使無類型Python代碼被視爲默認完全動態的機構。

Mypy帶有各種命令行參數,你可以嘗試強制它檢查無類型函數的內容(我相信它是--check-untyped-defs?),儘管它不會試圖推斷出什麼返回類型是。

+0

需要注意的是(至少在我的機器上)的@overload事項的順序。如果切換順序,那麼我得到錯誤:「__getitem__」的簽名與第一個@overload上的超類型「Sequence」不兼容。 –