PyCharm type hinting custom list iteration

So I created a custom type that subclasses from list, which works well in most cases; though I noticed that Pycharm has trouble figuring out type hint when iterating over an object of the custom class.

Here's how I define it:

class MyList(list):

    def as_tuple(self):
        return tuple(self)

Then to test with, I just create a simple class A and then create a bunch of A objects:

from dataclasses import dataclass

@dataclass
class A:
    f1: str
    f2: int


objects: MyList[A] = MyList([
    A('hello', 111),
    A('world', 222)
])

I think the type hint might be unnecessary, as it looks like PyCharm correctly identifies it as MyList[A] in any case.

Accessing an element appears to work fine for type hinting purposes: enter image description here

But PyCharm can't seem to offer type hints when iterating over the list type:

enter image description here

What I ended up doing so far is using typing.Generic in the class definition. I found out I also need to implement my own __iter__ method in the class, simply so I can type annotate the return type; otherwise, it doesn't seem to work without it.

Here's my new changes so far:

from typing import Generic, TypeVar, Iterator


T = TypeVar('T')


class MyList(list, Generic[T]):

    def __iter__(self) -> Iterator[T]:
        return super().__iter__()

    ...

Which apparently seems to do the trick: enter image description here

So the question is, is there an easier way to get type hint for iteration in PyCharm over a list subclass without needing to set it up the way I have above?

The other main concern I have is a slight hit to performance. For example, I've timed the iterating using the following code as an example, and found that the second approach I've used is slower; this is not surprising though, since I've implemented a custom __iter__, which introduces an additional method call.

from timeit import timeit

print(timeit("for o in objects: ...", globals=globals()))

I'm tempted to use something complicated like a metaclass solution or else maybe just re-assigning the MyList.__iter__ method outside the class definition, but curious to see if anyone has any alternatives to the approach that I've used above.


Solution 1:

You were on the right path but it's a little confusing how python type hinting works.

All you need to do is this and you dont need to override __iter__.

class MyList(list[T]):
    ...

Why this works is cause you are telling PyCharm that you are creating a class that inherits the abilities of a generic list.

On the other hand inheriting list and Generic you tell PyCharm you want to inherit the abilities of list but not the abilities of a generic list. Instead its a class that supports generics and has list abilities.

As for performance, this will still hurt performance and that's ok. Python is not known for being performant and if your code really needs to be fast then you should either look into writing it in Cython or C and importing it into your script or migrating completely away from Python.