使用
TypeVar
和Generic
,我可以使用可以推断类型的方法创建类,例如:
from typing import TypeVar, Generic
T = TypeVar('T')
class Box(Generic[T]):
def __init__(self, content: T) -> None:
self.content = content
Box(1) # OK, inferred type is Box[int]
是否可以推断类成员的类型?假设我有不同类型的运动员统计数据:
from abc import ABC
from typing import Generic, TypeVar, Type
class Stats(ABC):
pass
class BaseballStats(Stats):
@property
def batting_average(self) -> float:
return 0.314
class FootballStats(Stats):
@property
def yards(self) -> int:
return 314
现在我想为运动员定义一个抽象类:
S = TypeVar('S', bound=Stats)
class Athlete(ABC, Generic[S]):
_stats_type: Type[S]
@property
def stats(self) -> S:
return self._stats_type()
如果我尝试创建一个 BaseballPlayer
并让它通过分配给
_stats_type
来推断类型,它不会:
class BaseballPlayer(Athlete):
_stats_type = BaseballStats
bo = BaseballPlayer()
# reveal_type(bo.stats) --> gives `Any`; type was not inferred
print(bo.stats.batting_average) # works.
如果我指定 Athlete
的类型但不分配
_stats_type
,它将不会运行。
class FootballPlayer(Athlete[FootballStats]):
pass
bo = FootballPlayer()
# reveal_type(bo.stats) --> gives `FootballStats` because I explcitly called it out
print(bo.stats.yards) # AttributeError: 'FootballPlayer' object has no attribute '_stats_type'
有没有一种替代方案不需要我将统计类型放在两个地方?即,我可以避免这样做吗?
class FootballPlayer(Athlete[FootballStats]):
_stats_type = FootballStats
Athlete
的类型,否则 mypy 会抱怨以下内容:
error: Missing type parameters for generic type "Athlete" [type-arg]
现在,与您的 Box
示例相反,您的问题是您希望在运行时拥有类型(以便您可以在
stats
中实例化它)。我的经验(也被其他人分享,请参见例如
beartype)是,在运行时使用类型提示至少可以说有点笨拙。
话虽如此,您可能可以执行如下操作,在运行时从您的类继承的Athlete
获取类型。它基于来自PEP 560
的
__orig_bases__
。
from typing import cast, get_args, get_origin
class Athlete(ABC, Generic[S]):
@property
def _stats_type(self) -> Type[S]:
for orig_bases in self.__orig_bases__:
if get_origin(orig_bases) == Athlete:
return cast(Type[S], get_args(orig_bases)[0])
raise RuntimeError # should not happen
@property
def stats(self) -> S:
return self._stats_type()
class FootballPlayer(Athlete[FootballStats]):
pass
bo = FootballPlayer()
#reveal_type(bo.stats) --> gives `FootballStats`
print(bo.stats.yards)
但请注意 PEP 发出的关于 __orig_bases__
的以下警告:
注意:这两个方法名称保留供类型模块和通用类型机器使用,不鼓励任何其他使用。