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

Sun*_*bel 5 abstract-class types python-3.x 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
Run Code Online (Sandbox Code Playgroud)

运行mypy script.py收益:

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

我不明白此错误消息,并且很难找到有关该错误的任何文档。有什么方法可以注释该函数,以便进行mypy类型检查吗?

附带说明一下,PyCharm的类型检查器是我使用最多的类型检查f,没有错误。

cha*_*rik 5

看起来 mypy 确实有点偏向于以这种方式使用抽象基类,尽管正如您所展示的那样,存在有效的用例。

您可以通过使工厂函数成为抽象类上的类方法来解决此问题。如果在风格上您希望将顶级函数用作工厂,那么您可以为类方法创建别名。

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 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
Run Code Online (Sandbox Code Playgroud)

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

或者,如果您愿意放弃由 提供的运行时检查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 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
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为@abstractmethod即使类没有继承自abc.ABC. 但是请注意,如果您使用 python 执行程序,您将不再收到有关实例化Bad类而不实现其抽象方法的错误。


Blu*_*orn 3

mypy 中存在关于此不当行为(恕我直言)的github 问题。基本上,Type[_T]作为_TTypeVar 永远不会接受抽象类。

\n

我见过的唯一明智的解决方案是禁用此错误,例如通过将其包含在文件中mypy.ini

\n
[mypy]\n# Allows Type[T] to refer to abstract classes, which is not otherwise supported.\n# See https://github.com/python/mypy/issues/4717\ndisable_error_code = type-abstract\n
Run Code Online (Sandbox Code Playgroud)\n

引用讨论

\n
\n

既然 #14619 已合并,默认情况下禁用类型抽象错误代码是否可以解决所有主要问题,或者是否还需要做其他事情?

\n
\n

我稍后会将我的 2\xc2\xa2 添加到票证中,希望他们能解决这个问题。

\n