我正在编写一个库,在这里我需要一个采用(可能)抽象类型的方法,并返回该类型的具体子类型的实例:
# 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
可以键入check this吗?附带说明一下,PyCharm的类型检查器(这是我最常使用的类型)对
f
进行类型检查没有任何错误。
最佳答案
确实,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
请注意,没有更多的工作,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
这是可行的,因为mypy会检查标有
@abstractmethod
的方法,即使该类未从abc.ABC
继承。但是请注意,如果您使用python执行程序,则在不实现其抽象方法的情况下,实例化Bad
类将不再出错。
关于python-3.x - 您如何用mypy注释抽象类的类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48349054/