Bleyddyn
Bleyddyn

Reputation: 387

Fix multiple inheritance with generic base classes

from typing import Generic, TypeVar, Any

R = TypeVar('R')
X = TypeVar('X')

class SizedIterator(Generic[X]):
    def __init__(self) -> None:
        pass

class TfmIterator(Generic[R],  SizedIterator):
    def __init__(self) -> None:
        pass

The above is a much simplified version of the code in https://github.com/autorope/donkeycar/blob/dev/donkeycar/pipeline/sequence.py.

Apparently that code worked fine in Python 3.6 and/or 3.7. However, it gives the following error when I try to run it in Python 3.9:

Traceback (most recent call last):
  File "/Users/Shared/Personal/mycar/simple1.py", line 10, in <module>
    class TfmIterator(Generic[R],  SizedIterator):
TypeError: Cannot create a consistent method resolution
order (MRO) for bases Generic, SizedIterator

My question is what can I do to keep the same type hints without running into the MRO error?

Upvotes: 2

Views: 1165

Answers (2)

o11c
o11c

Reputation: 16056

Factoring out an additional base class should not be necessary. The key rules are:

  • Generic must always be the last base class.
  • When doing diamond inheritance in general, you may have to explicitly write much of the MRO as immediate parents even if they are already indirect parents. Printing out the MROs of the parents may help you figure out what you need to write to get the MRO you want.
  • if the ultimately-derived class should not accept generic parameters and you require your rightmost base to be something that does not derive from Generic, then you will have to add a "bogus" base class that is actually an alias for Generic[T] but uses if TYPE_CHECKING to replace itself with an empty class for type analyzers.

Upvotes: 4

Oleksii Tambovtsev
Oleksii Tambovtsev

Reputation: 2834

You can try this:

from typing import Generic, TypeVar, Any

R = TypeVar('R')
X = TypeVar('X')


class SizedIterator(Generic[X]):
    def __init__(self) -> None:
        pass


class NewIterator(Generic[R]):
    def __init__(self) -> None:
        pass


class TfmIterator(NewIterator, SizedIterator):
    def __init__(self) -> None:
        pass

Upvotes: 1

Related Questions