当我为抽象类的子类定义类型别名时,遇到循环导入错误。
这是我想要实现的目标的一个例子:
#abstract_file_builder.py
from abc import ABC, abstractmethod
from typing import Generic, MutableSequence, TypeVar
from mymodule.type_a_file_builder import TypeARow
from mymodule.type_b_file_builder import TypeBRow
GenericRow = TypeVar("GenericRow", TypeARow, TypeBRow)
class AbstractFileBuilder(ABC, Generic[GenericRow]):
...
@abstractmethod
def generate_rows(
self,
) -> MutableSequence[GenericRow]:
pass
#type_a_file_builder.py
from typing import Any, MutableSequence
from mymodule.abstract_file_builder import AbstractFileBuilder
TypeARow = MutableSequence[Any]
class TypeAFileBuilder(AbstractFileBuilder[TypeARow]):
...
def generate_rows(
self,
) -> MutableSequence[TypeARow]:
... # Code logic for TypeA
return rows
#type_b_file_builder.py
from typing import MutableSequence, Union
from mymodule.abstract_file_builder import AbstractFileBuilder
TypeBRow = MutableSequence[Union[int, float]]
class TypeBFileBuilder(AbstractFileBuilder[TypeBRow]):
...
def generate_rows(
self,
) -> MutableSequence[TypeBRow]:
... # Code logic for TypeB
return rows
解决这个问题最Pythonic的方法是什么?
我知道我可以使用
TYPE_CHECKING
变量来避免运行时导入,但这感觉就像一个补丁而不是一个好的解决方案。
可以解决问题的另一件事是在抽象类中定义类型别名,但这会破坏拥有抽象类并且不必知道下面实现了什么的整个目的。
但是我不确定是否可以在
abstract_file_builder.py
文件中执行某种形式的“抽象”类型别名,然后将 TypeARow 和 TypeBRow 类型声明为该抽象类型的子级。
我必须注意,该解决方案必须至少与
Python 3.9
一起工作。如果它支持回到 3.7
的版本会更好,但不是非常必要。
通过将以下行放入抽象类中,您可以将具体子类的信息添加到抽象类中:
GenericRow = TypeVar("GenericRow", TypeARow, TypeBRow)
我觉得这不太对劲。我认为您应该删除这种依赖关系,因为这意味着您可能必须再次更改抽象方法的返回类型的定义,以防您稍后添加另一个子类。我只会在您认为您的子类非常固定且返回类型未更改或添加新子类的情况下才会考虑这一点。
在这种情况下,您可以将三个类放在一个文件中,并将返回类型的定义放在抽象类的定义之前。
但是因为我们通常不知道稍后是否添加其他子类(否则我们为什么要关心创建抽象超类),我认为超类不应该了解子类。
在这种情况下,我宁愿使用更广泛的类型来输入抽象方法,例如像这样:
#abstract_file_builder.py
from abc import ABC, abstractmethod
from typing import Generic, MutableSequence, TypeVar
class AbstractFileBuilder(ABC):
...
@abstractmethod
def generate_rows(
self,
) -> MutableSequence[Any]:
pass