Pycharm 类型提示警告 类 而不是实例

Pycharm type hints warning for classes instead of instances

我试图理解为什么 pycharm 在使用以静态方法作为参数的抽象 class 实现时警告我类型错误。

为了演示我会做一个简单的例子。假设我有一个带有一个方法的抽象 class,一个实现(继承)这个类似接口的抽象 class 的 class,以及一个获取应该用作参数的实现的方法。

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")

请注意,在 print_greeting 的类型提示中,我使用了 GreetingMakerBase,因为 isinstance(hello_maker, GreetingMakerBase) is True Pycharm 没有抱怨它。

问题是我有很多 class 的实现,不想为每个实现创建一个实例,所以我将这个 make_greeting 方法设为静态,如下所示:

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")

这仍然以相同的方式工作,但显然因为函数调用中的参数现在是 class 名称而不是它的实例,Pycharm 抱怨说: Expected type 'GreetingMakerBase', got 'Type[HelloGreetingMaker]' instead.

有没有一种方法可以解决此警告而无需实例化 HelloGreetingMaker class?

您没有创建实例,并且您的类型提示暗示该函数只接受实例(某种类型 GreetingMakerBase,而不是 GreetingMakerBase 本身或子 class的)。

如果你想指定只有 GreetingMakerBase 本身是一个可接受的论点,为什么要把它作为一个论点呢?只需在内部调用 class 的函数即可。

无论如何,python 3.8 有一些新的打字改进可以帮助您。您可以指定 literal type hint:

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

如果您需要在其他(早于 3.8)python 版本中支持此类型提示,则必须安装 typing extensions

pip install typing-extensions

当您执行 print_greeting(HelloGreetingMaker, "John") 时,您不会尝试传入 HelloGreetingMaker 的 实例 。相反,您传递的是 class 本身。

我们输入的方式是使用 Type[T],它指定您需要 T 的类型,而不是 T 的实例。例如:

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")

请注意,Type[HelloGreetingMaker] 被认为与 Type[GreetingMakerBase] 兼容——Type[T] 是关于 T 的协变。

如果您想了解更多,Python docs on the typing module and the mypy docs 有更多详细信息和示例。