我正在编写一个库,我需要一个采用(可能)抽象类型的方法,并返回该类型的具体子类型的实例:
# 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
没有错误。
答案 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
类时出错。