你如何用mypy注释抽象类的类型?

时间:2018-01-19 20:29:22

标签: python-3.x types abstract-class mypy

我正在编写一个库,我需要一个采用(可能)抽象类型的方法,并返回该类型的具体子类型的实例:

# script.py
from typing import Type
from abc import ABC, abstractmethod


class AbstractClass(ABC):
    @abstractmethod
    def abstract_method(self):
        pass

T = TypeVar('T', bound=AbstractClass)

def f(c: Type[T]) -> T:
    # find concrete implementation of c based on
    # environment configuration
    ...


f(AbstractClass)  # doesn't type check

运行mypy script.py会产生:

error: Only concrete class can be given where "Type[AbstractClass]" is expected

我不理解此错误消息,并且很难找到任何文档。有没有办法注释函数,以便mypy键入来检查这个?

作为附注,PyCharm的类型检查器,这是我最常用的类型,键入检查f没有错误。

1 个答案:

答案 0 :(得分:1)

看起来mypy似乎有点偏向于使用这种方式使用抽象基类,尽管你演示了有效的用例。

您可以通过使您的工厂函数成为抽象类的类方法来解决此问题。如果风格上你想要一个顶层函数作为工厂,那么你可以创建一个类方法的别名。

请注意,如果没有更多工作,mypy无法知道工厂函数创建了哪个具体类,它只会知道它与AbstractClass兼容。

from typing import TYPE_CHECKING
from abc import ABC, abstractmethod


class AbstractClass(ABC):
    @abstractmethod
    def abstract_method(self):
        raise NotImplementedError

    @classmethod
    def make_concrete(cls) -> 'AbstractClass':
        # find concrete implementation of c based on
        # environment configuration
        return A()


class A(AbstractClass):
    def abstract_method(self):
        print("a")

# make alias
f = AbstractClass.make_concrete
x = f()
if TYPE_CHECKING:
    reveal_type(x)  # AbstractClass

或者,如果您愿意放弃abc.ABC提供的运行时检查,您可以获得更接近原始设计的内容:

from typing import TYPE_CHECKING
from abc import abstractmethod


class AbstractClass:  # do NOT inherit from abc.ABC
    @abstractmethod
    def abstract_method(self):
        raise NotImplementedError


class A(AbstractClass):
    def abstract_method(self):
        print("a")


class Bad(AbstractClass):
    pass


def f() -> AbstractClass:
    # find concrete implementation of c based on
    # environment configuration
    pass

b = Bad()  # mypy displays an error here:  Cannot instantiate abstract class 'Bad' with abstract attribute 'abstract_method'

x = f()
if TYPE_CHECKING:
    reveal_type(x)  # AbstractClass

这是有效的,因为即使该类没有从@abstractmethod继承,mypy也会检查标有abc.ABC的方法。但是请注意,如果使用python执行程序,则不会在没有实现其抽象方法的情况下实例化Bad类时出错。