vpeiter
vpeiter

Reputation: 63

Pycharm type hints warning for classes instead of instances

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?

Upvotes: 1

Views: 1447

Answers (2)

Michael0x2a
Michael0x2a

Reputation: 63978

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.

Upvotes: 4

Ofer Sadan
Ofer Sadan

Reputation: 11922

You did not create an instance, and your type hints implies that the function only accepts instances (something of a type GreetingMakerBase, and not GreetingMakerBase itself or a subclass of it).

If you want to specify that only GreetingMakerBase itself is an acceptable argument, why have it as an argument at all? Just have the function call that class internally.

In any case, python 3.8 has some new typing improvements that can help you. You can specify a literal type hint:

from typing import Literal
def print_greeting(maker: Literal[GreetingMakerBase], name):
    print(maker.make_greeting(name))

If you need to support this type-hint in other (earlier than 3.8) python versions, you will have to install the typing extensions:

pip install typing-extensions

Upvotes: 0

Related Questions