I am trying to understand why pycharm warns me of wrong type when using an implementation of an abstract class with static method as parameter.
To demonstrate I will make a simple example. Let's say I have an abstract class with one method, a class that implements (inherits) this interface-like abstract class, and a method that gets the implementation it should use as parameter.
import abc
class GreetingMakerBase(abc.ABC):
@abc.abstractmethod
def make_greeting(self, name: str) -> str:
""" Makes greeting string with name of person """
class HelloGreetingMaker(GreetingMakerBase):
def make_greeting(self, name: str) -> str:
return "Hello {}!".format(name)
def print_greeting(maker: GreetingMakerBase, name):
print(maker.make_greeting(name))
hello_maker = HelloGreetingMaker()
print_greeting(hello_maker, "John")
Notice that in the type hinting of print_greeting
I used GreetingMakerBase
, and because isinstance(hello_maker, GreetingMakerBase) is True
Pycharm is not complaining about it.
The problem is that I have many implementations of my class and dont want to make an instance of each, so I will make this make_greeting
method static, like this:
class GreetingMakerBase(abc.ABC):
@staticmethod
@abc.abstractmethod
def make_greeting(name: str) -> str:
""" Makes greeting string with name of person """
class HelloGreetingMaker(GreetingMakerBase):
@staticmethod
def make_greeting(name: str) -> str:
return "Hello {}!".format(name)
def print_greeting(maker: GreetingMakerBase, name):
print(maker.make_greeting(name))
print_greeting(HelloGreetingMaker, "John")
This still works the same way, but apparently because the parameter in the function call is now the class name instead of an instance of it, Pycharm complains that:
Expected type 'GreetingMakerBase', got 'Type[HelloGreetingMaker]' instead.
Is there a way I can solve this warning without having to instantiate the HelloGreetingMaker
class?
When you are doing print_greeting(HelloGreetingMaker, "John")
, you are not trying to pass in an instance of HelloGreetingMaker. Rather, you're passing in the class itself.
The way we type this is by using Type[T]
, which specifies you want the type of T, rather then an instance of T. So for example:
from typing import Type
import abc
class GreetingMakerBase(abc.ABC):
@staticmethod
@abc.abstractmethod
def make_greeting(name: str) -> str:
""" Makes greeting string with name of person """
class HelloGreetingMaker(GreetingMakerBase):
@staticmethod
def make_greeting(name: str) -> str:
return "Hello {}!".format(name)
def print_greeting(maker: Type[GreetingMakerBase], name):
print(maker.make_greeting(name))
# Type checks!
print_greeting(HelloGreetingMaker, "John")
Note that Type[HelloGreetingMaker]
is considered to be compatible with Type[GreetingMakerBase]
-- Type[T]
is covariant with respect to T.
The Python docs on the typing module and the mypy docs have more details and examples if you want to learn more.