10

I have a class that inherits from MutableSequence like this:

class QqTag(MutableSequence):
    def __init__(self):
        self._children = []
    def __getitem__(self, idx: int) -> 'QqTag':
        return self._children[idx]

mypy complains that Signature of "__getitem__" incompatible with supertype "Sequence".

In Sequence, this method is defined as:

@abstractmethod
def __getitem__(self, index):
    raise IndexError

So, what's the problem and why mypy isn't happy with my implementation?

Joël
  • 2,723
  • 18
  • 36
Ilya V. Schurov
  • 7,687
  • 2
  • 40
  • 78
  • Just a guess - but what happens if you remove the type annotation...? – Jon Clements Oct 11 '17 at 10:03
  • 1
    @JonClements: this should be possible to specify, however. – Martijn Pieters Oct 11 '17 at 10:06
  • @Martijn yeah... was just a wild guess that if it's checking the type annotation and moaning that it thinks it's being restrictive from the base - after all, the `idx` needn't be an `int` etc... – Jon Clements Oct 11 '17 at 10:08
  • 1
    Relevant [typing issue](https://github.com/python/typing/issues/241), summarising the [mypy issue](https://github.com/python/mypy/issues/1237). – Martijn Pieters Oct 11 '17 at 10:14
  • If I replace `idx: int` with simple `idx`, it doesn't complain. Possible a bug in `collections`? I believe `Sequence`s should be indexed with `int`s anyway. – Ilya V. Schurov Oct 11 '17 at 10:14
  • @IlyaV.Schurov not necessarily - a `slice` is also valid (although technically - anything is valid depending on what you want to do with it - but with things that are going to emulate `list` like behaviour then it's normally `int`s or `slice`s) – Jon Clements Oct 11 '17 at 10:16
  • @JonClements, okay, I agree about slices. Anyway, are there any way to make mypy happy and keep `idx: int` restriction? – Ilya V. Schurov Oct 11 '17 at 10:19
  • @IlyaV.Schurov look at the issues that Martijn has pointed out :) – Jon Clements Oct 11 '17 at 10:19
  • @MartijnPieters thanks, I read this and understood the actual problem with my code. However, I'm still cannot understand how to do it properly. I'll probably have to ask another question for that… – Ilya V. Schurov Oct 11 '17 at 13:32
  • 1
    @MartijnPieters could you please look at my [follow-up question](https://stackoverflow.com/questions/46690012/subclassing-sequence-with-proper-type-hints-in-python)? – Ilya V. Schurov Oct 12 '17 at 17:56
  • @IlyaV.Schurov: isn't that essentially the same question? Sorry, I do not currently have an answer. – Martijn Pieters Oct 12 '17 at 18:03

2 Answers2

7

As mentioned in comments, a typeof slice can also be passed. Ie, change idx: int to idx: Union[int, slice].

This will make mypy happy (at least on my machine ;):

class QqTag(MutableSequence):
    def __init__(self):
        self._children = []

    def __getitem__(self, idx: Union[int, slice]) -> 'QqTag':
        return self._children[idx]
Zachary Ryan Smith
  • 2,688
  • 1
  • 20
  • 30
2

Another option is to use the typing.overload decorator.

from typing import overload

class QqTag(MutableSequence):
    def __init__(self):
        self._children = []
    @overload
    def __getitem__(self, i: int) -> 'QqTag':
        ...
    @overload
    def __getitem__(self, s: slice) -> list['QqTag']:
        ...
    def __getitem__(self, idx):
        return self._children[idx]

See examples from typing or PEP 484 -- Type Hints.