Get Off My Lawn
Get Off My Lawn

Reputation: 36311

Class method override with any type of property

I am trying to create a method override, and I am not sure how to make it so that the override doesn't throw the following error:

Method "main" overrides class "App" in an incompatible manner
  Parameter 2 type mismatch: base parameter is type "object", override parameter is type "Tuple[str, int, int]"
    "object" is incompatible with "Tuple[str, int, int]"

I Understand that the two types are different, but since object is a base class shouldn't this be allowed? I have strict turned on, so an error is thrown when I use any which is what I would like to keep turned on.

class App:
    @abstractmethod
    def main(self, params: object):
        pass

class MyClass(App):
    def main(self, params: Tuple[str, int, int]):
        # Do something

What is the proper way to override a method and allow a parameter to be of any type (while using strict typing)?

Upvotes: 0

Views: 962

Answers (1)

Silvio Mayolo
Silvio Mayolo

Reputation: 70287

You've got the variance backwards.

class App:
  @abstractmethod
  def main(self, params: object):
    pass

This is a promise. It says "any class which implements App has a main which works on all objects. Not some of them. All of them.

Then you come along and write

class MyClass(App):
  def main(self, params: Tuple[str, int, int]):
    # Do something

So MyClass has a main which works on a specific type of parameter, not all of them. Your type checker correctly reports that this is not what you promised earlier. You could do the opposite thing: If App requires Tuple[str, int, int], then MyClass could implement a version for object, since that includes Tuple[str, int, int] and then some.

But for your case, you probably want generics.

from typing import TypeVar, Generic

_T_contra = TypeVar("_T_contra", contravariant=True)

class App(Generic[_T_contra]):
  @abstractmethod
  def main(self, params: _T_contra) -> None:
    pass

class MyClass(App[Tuple[str, int, int]]):
  def main(self, params: Tuple[str, int, int]):
    # Do something

Now MyClass doesn't claim to work for all objects. It claims to work for this specific type, so it derives from a specific App.

For a discussion of why it's _T_contra (a contravariant type parameter), you can read my other answer where I go into more detail on variance annotations in Python.

Upvotes: 4

Related Questions