【问题标题】:Pycharm type hints warning for classes instead of instancesPycharm 类型提示警告类而不是实例
【发布时间】:2020-02-18 08:10:59
【问题描述】:

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

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

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 没有抱怨它。

问题是我的类有很多实现,不想为每个类创建一个实例,所以我将这个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")

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

有没有一种方法可以解决这个警告而不必实例化 HelloGreetingMaker 类?

【问题讨论】:

    标签: python-3.x pycharm static-methods type-hinting abstract-methods


    【解决方案1】:

    当你在做print_greeting(HelloGreetingMaker, "John") 时,你并没有尝试传入一个HelloGreetingMaker 的instance。相反,您是在传递类本身。

    我们输入 this 的方式是使用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 modulemypy docs 有更多详细信息和示例。

    【讨论】:

      【解决方案2】:

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

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

      无论如何,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
      

      【讨论】:

      • 感谢您的快速答复!我不希望函数只接受 GreetingMakerBase 本身,我希望它只接受它的子类(如接口的实现)。在我的实际项目中,有很多子类,我只使用了一个,因为这是一个简化的示例。
      • 文字类型提示在这里不起作用——它们只能接受文字值,例如字符串、整数和布尔值。还有枚举值,它们不是真正的文字,但无论如何。 The PEP 对此进行了详细介绍。您正在寻找 typing.Type 代替。
      猜你喜欢
      • 1970-01-01
      • 2019-05-27
      • 2020-05-22
      • 2017-04-22
      • 2014-09-01
      • 2019-02-23
      • 2013-12-26
      • 2021-12-07
      • 2015-11-23
      相关资源
      最近更新 更多