2017-10-11 2 views
1

Je suis en train de mettre en œuvre une sorte de classe de séquence personnalisée en Python:Subclassing Séquence avec des notes de type approprié en 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) 

Maintenant, je veux vérifier que mypy est conscient du fait que les éléments de MySequence sont des éléments de type T:

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

il échoue: mypy ne sait rien sur les éléments de foo. Le même exemple pour Sequence ordinaire fonctionne:

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

Si je suis en train d'ajouter des annotations de type à __getitem__ mise en œuvre, j'ai une autre erreur:

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

J'ai aussi essayé

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

comme idx peut être une tranche et dans ce cas mon code retournera une séquence au lieu d'un élément. Il échoue soit avec le même message. Comme discuté dans my previous question, il ya un open discussion sur des questions comme ça.

Cependant, je me demande encore, est-il possible de créer des types de séquence personnalisés qui permettent mypy d'extraire des informations sur le type de ses éléments, comme dans mon exemple?

Répondre

2

Dans ce cas, la bonne chose à faire est de remplacer correctement le exact signature for __getitem__, y compris les surcharges.

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]) 

(Notez que je fait le covariant TypeVar. Ce n'est pas, strictement parlant, nécessaire, mais si le conteneur est effectivement censé représenter une sorte de structure « lecture seule », nous pourrions aussi bien pour une flexibilité maximale)


note:. le fait que mypy décide que le type de retour est Tout dans le premier exemple d'un comportement attendu. Selon le PEP 484, toute méthode ou signature sans annotations de type est traitée comme si les types d'argument et de retour étaient tous Any.

Ceci est un mécanisme conçu pour que le code Python non typé soit traité comme entièrement dynamique par défaut. Mypy est livré avec une variété d'arguments de ligne de commande que vous pouvez utiliser pour essayer de le contraindre à vérifier le contenu des fonctions non typées de toute façon (je crois que c'est --check-untyped-defs?), Mais il ne tentera pas de déduire ce que le le type de retour est.

+0

Notez que (au moins sur ma machine) l'ordre de @overload est important. Si je commute l'ordre, j'obtiens 'error: Signature de" __getitem__ "incompatible avec supertype" Sequence "' sur le premier @overload. –